Repository: farcaster-project/farcaster-node
Pull request #818 (2022-12-12 19:48:38)

Grpc: Add network selectors to data queries
This allows the grpc server to filter the response contents by certain networks. When calling ListOffers, Checkpoints, FundingAddresses, and NeedsFunding the response is now filtered by the network.


Patch:
diff --git a/src/grpcd/proto/farcaster.proto b/src/grpcd/proto/farcaster.proto
index 7be2b3dc..6c4f0f39 100644
--- a/src/grpcd/proto/farcaster.proto
+++ b/src/grpcd/proto/farcaster.proto
@@ -23,14 +23,14 @@ service Farcaster {
 
 message HealthCheckRequest {
     uint32 id = 1;
-    HealthCheckSelector selector = 2;
+    NetworkSelector selector = 2;
 }
 
-enum HealthCheckSelector {
-    CHECK_ALL = 0;
-    CHECK_MAINNET = 1;
-    CHECK_TESTNET = 2;
-    CHECK_LOCAL = 3;
+enum NetworkSelector {
+    ALL_NETWORKS = 0;
+    MAINNET_NETWORKS = 1;
+    TESTNET_NETWORKS = 2;
+    LOCAL_NETWORKS = 3;
 }
 
 message HealthCheckResponse {
@@ -124,7 +124,8 @@ message PeersResponse {
 
 message ListOffersRequest {
     uint32 id = 1;
-    OfferSelector selector = 2;
+    OfferSelector offer_selector = 2;
+    NetworkSelector network_selector = 3;
 }
 
 enum OfferSelector {
@@ -141,7 +142,8 @@ message ListOffersResponse {
 
 message CheckpointsRequest {
     uint32 id = 1;
-    CheckpointSelector selector = 2;
+    CheckpointSelector checkpoint_selector = 2;
+    NetworkSelector network_selector = 3;
 }
 
 message CheckpointsResponse {
@@ -178,6 +180,7 @@ message RestoreCheckpointResponse {
 message FundingAddressesRequest {
     uint32 id = 1;
     Blockchain blockchain = 2;
+    NetworkSelector network_selector = 3;
 }
 
 message FundingAddressesResponse {
@@ -320,6 +323,7 @@ message ConnectSwapResponse {
 message NeedsFundingRequest {
     uint32 id = 1;
     Blockchain blockchain = 2;
+    NetworkSelector network_selector = 3;
 }
 
 message NeedsFundingResponse {
diff --git a/src/grpcd/runtime.rs b/src/grpcd/runtime.rs
index 9d8426d8..fe62f620 100644
--- a/src/grpcd/runtime.rs
+++ b/src/grpcd/runtime.rs
@@ -16,6 +16,7 @@ use crate::bus::Failure;
 use crate::bus::HealthCheckSelector;
 use crate::bus::OptionDetails;
 use crate::bus::Outcome;
+use crate::grpcd::runtime::farcaster::NetworkSelector;
 use crate::service::Endpoints;
 use crate::swapd::StateReport;
 use crate::syncerd::SweepAddressAddendum;
@@ -97,6 +98,17 @@ impl From<farcaster::Network> for Network {
     }
 }
 
+impl From<NetworkSelector> for Option<Network> {
+    fn from(t: NetworkSelector) -> Option<Network> {
+        match t {
+            NetworkSelector::AllNetworks => None,
+            NetworkSelector::MainnetNetworks => Some(Network::Mainnet),
+            NetworkSelector::TestnetNetworks => Some(Network::Testnet),
+            NetworkSelector::LocalNetworks => Some(Network::Local),
+        }
+    }
+}
+
 impl From<Network> for farcaster::Network {
     fn from(t: Network) -> farcaster::Network {
         match t {
@@ -136,6 +148,23 @@ impl From<farcaster::OfferSelector> for OfferStatusSelector {
     }
 }
 
+impl From<FundingInfo> for farcaster::FundingInfo {
+    fn from(t: FundingInfo) -> farcaster::FundingInfo {
+        match t {
+            FundingInfo::Bitcoin(b_info) => farcaster::FundingInfo {
+                swap_id: b_info.swap_id.to_string(),
+                address: b_info.address.to_string(),
+                amount: b_info.amount.as_sat(),
+            },
+            FundingInfo::Monero(m_info) => farcaster::FundingInfo {
+                swap_id: m_info.swap_id.to_string(),
+                address: m_info.address.to_string(),
+                amount: m_info.amount.as_pico(),
+            },
+        }
+    }
+}
+
 impl From<Outcome> for farcaster::Outcome {
     fn from(t: Outcome) -> farcaster::Outcome {
         match t {
@@ -350,17 +379,17 @@ impl From<StateReport> for farcaster::State {
     }
 }
 
-impl From<farcaster::HealthCheckSelector> for HealthCheckSelector {
-    fn from(s: farcaster::HealthCheckSelector) -> Self {
+impl From<farcaster::NetworkSelector> for HealthCheckSelector {
+    fn from(s: farcaster::NetworkSelector) -> Self {
         match s {
-            farcaster::HealthCheckSelector::CheckAll => HealthCheckSelector::All,
-            farcaster::HealthCheckSelector::CheckLocal => {
+            farcaster::NetworkSelector::AllNetworks => HealthCheckSelector::All,
+            farcaster::NetworkSelector::LocalNetworks => {
                 HealthCheckSelector::Network(Network::Local)
             }
-            farcaster::HealthCheckSelector::CheckMainnet => {
+            farcaster::NetworkSelector::MainnetNetworks => {
                 HealthCheckSelector::Network(Network::Mainnet)
             }
-            farcaster::HealthCheckSelector::CheckTestnet => {
+            farcaster::NetworkSelector::TestnetNetworks => {
                 HealthCheckSelector::Network(Network::Testnet)
             }
         }
@@ -536,14 +565,18 @@ impl Farcaster for FarcasterService {
         debug!("Received a grpc request: {:?}", request);
         let ListOffersRequest {
             id,
-            selector: grpc_offer_selector,
+            offer_selector: grpc_offer_selector,
+            network_selector: grpc_network_selector,
         } = request.into_inner();
-        let selector = farcaster::OfferSelector::from_i32(grpc_offer_selector)
-            .ok_or(Status::invalid_argument("selector"))?
+        let offer_selector = farcaster::OfferSelector::from_i32(grpc_offer_selector)
+            .ok_or(Status::invalid_argument("offer_selector"))?
             .into();
+        let network_selector: NetworkSelector =
+            farcaster::NetworkSelector::from_i32(grpc_network_selector)
+                .ok_or(Status::invalid_argument("network_selector"))?;
         let oneshot_rx = self
             .process_request(BusMsg::Bridge(BridgeMsg::Info {
-                request: InfoMsg::ListOffers(selector),
+                request: InfoMsg::ListOffers(offer_selector),
                 service_id: ServiceId::Farcasterd,
             }))
             .await?;
@@ -553,6 +586,10 @@ impl Farcaster for FarcasterService {
                     id,
                     offers: offers
                         .drain(..)
+                        .filter(|o| {
+                            network_selector == NetworkSelector::AllNetworks
+                                || Some(o.details.offer.network) == network_selector.into()
+                        })
                         .map(|o| OfferInfo::from(o.details))
                         .collect(),
                 };
@@ -595,13 +632,21 @@ impl Farcaster for FarcasterService {
         request: GrpcRequest<CheckpointsRequest>,
     ) -> Result<GrpcResponse<CheckpointsResponse>, Status> {
         debug!("Received a grpc checkpoints request: {:?}", request);
-        let CheckpointsRequest { id, selector } = request.into_inner();
+        let CheckpointsRequest {
+            id,
+            checkpoint_selector: grpc_checkpoint_selector,
+            network_selector: grpc_network_selector,
+        } = request.into_inner();
 
-        let selector = farcaster::CheckpointSelector::from_i32(selector)
-            .ok_or(Status::invalid_argument("selector"))?
+        let checkpoint_selector = farcaster::CheckpointSelector::from_i32(grpc_checkpoint_selector)
+            .ok_or(Status::invalid_argument("checkpoint_selector"))?
             .into();
 
-        let oneshot_rx = match selector {
+        let network_selector: NetworkSelector =
+            farcaster::NetworkSelector::from_i32(grpc_network_selector)
+                .ok_or(Status::invalid_argument("network_selector"))?;
+
+        let oneshot_rx = match checkpoint_selector {
             farcaster::CheckpointSelector::AllCheckpoints => {
                 self.process_request(BusMsg::Bridge(BridgeMsg::Info {
                     request: InfoMsg::RetrieveAllCheckpointInfo,
@@ -643,6 +688,10 @@ impl Farcaster for FarcasterService {
                     id,
                     checkpoint_entries: checkpoint_entries
                         .iter()
+                        .filter(|entry| {
+                            network_selector == NetworkSelector::AllNetworks
+                                || Some(entry.public_offer.offer.network) == network_selector.into()
+                        })
                         .map(|entry| farcaster::CheckpointEntry {
                             swap_id: format!("{:#x}", entry.swap_id),
                             offer: Some(entry.public_offer.clone().into()),
@@ -707,12 +756,17 @@ impl Farcaster for FarcasterService {
         let FundingAddressesRequest {
             id,
             blockchain: grpc_blockchain,
+            network_selector: grpc_network_selector,
         } = request.into_inner();
 
         let blockchain: Blockchain = farcaster::Blockchain::from_i32(grpc_blockchain)
             .ok_or(Status::invalid_argument("arbitrating blockchain"))?
             .into();
 
+        let network_selector: NetworkSelector =
+            farcaster::NetworkSelector::from_i32(grpc_network_selector)
+                .ok_or(Status::invalid_argument("network_selector"))?;
+
         let oneshot_rx = self
             .process_request(BusMsg::Bridge(BridgeMsg::Info {
                 request: InfoMsg::GetAddresses(blockchain),
@@ -725,6 +779,10 @@ impl Farcaster for FarcasterService {
                     id,
                     addresses: addresses
                         .iter()
+                        .filter(|a| {
+                            network_selector == NetworkSelector::AllNetworks
+                                || Some(Network::from(a.address.network)) == network_selector.into()
+                        })
                         .map(|a| AddressSwapIdPair {
                             address: a.address.to_string(),
                             address_swap_id: a.swap_id.map(|c| {
@@ -1022,12 +1080,17 @@ impl Farcaster for FarcasterService {
         let NeedsFundingRequest {
             id,
             blockchain: int_blockchain,
+            network_selector: grpc_network_selector,
         } = request.into_inner();
 
         let blockchain: Blockchain = farcaster::Blockchain::from_i32(int_blockchain)
             .ok_or(Status::invalid_argument("blockchain"))?
             .into();
 
+        let network_selector: NetworkSelector =
+            farcaster::NetworkSelector::from_i32(grpc_network_selector)
+                .ok_or(Status::invalid_argument("network_selector"))?;
+
         let oneshot_rx = self
             .process_request(BusMsg::Bridge(BridgeMsg::Info {
                 request: InfoMsg::NeedsFunding(blockchain),
@@ -1036,24 +1099,26 @@ impl Farcaster for FarcasterService {
             .await?;
 
         match oneshot_rx.await {
-            Ok(BusMsg::Info(InfoMsg::FundingInfos(infos))) => {
+            Ok(BusMsg::Info(InfoMsg::FundingInfos(mut infos))) => {
                 let reply = NeedsFundingResponse {
                     id,
                     funding_infos: infos
                         .swaps_need_funding
-                        .iter()
-                        .map(|info| match info {
-                            FundingInfo::Bitcoin(b_info) => farcaster::FundingInfo {
-                                swap_id: b_info.swap_id.to_string(),
-                                address: b_info.address.to_string(),
-                                amount: b_info.amount.as_sat(),
-                            },
-                            FundingInfo::Monero(m_info) => farcaster::FundingInfo {
-                                swap_id: m_info.swap_id.to_string(),
-                                address: m_info.address.to_string(),
-                                amount: m_info.amount.as_pico(),
-                            },
+                        .drain(..)
+                        .filter(|info| {
+                            network_selector == NetworkSelector::AllNetworks
+                                || match info {
+                                    FundingInfo::Bitcoin(b_info) => {
+                                        Some(Network::from(b_info.address.network))
+                                            == network_selector.into()
+                                    }
+                                    FundingInfo::Monero(m_info) => {
+                                        Some(Network::from(m_info.address.network))
+                                            == network_selector.into()
+                                    }
+                                }
                         })
+                        .map(farcaster::FundingInfo::from)
                         .collect(),
                 };
                 Ok(GrpcResponse::new(reply))
@@ -1071,7 +1136,7 @@ impl Farcaster for FarcasterService {
             selector: grpc_selector,
         } = request.into_inner();
 
-        let selector: HealthCheckSelector = farcaster::HealthCheckSelector::from_i32(grpc_selector)
+        let selector: HealthCheckSelector = farcaster::NetworkSelector::from_i32(grpc_selector)
             .ok_or(Status::invalid_argument("selector"))?
             .into();
 


Test patch:
diff --git a/tests/grpc.rs b/tests/grpc.rs
index 1b9d0520..436f3622 100644
--- a/tests/grpc.rs
+++ b/tests/grpc.rs
@@ -3,9 +3,9 @@ extern crate log;
 
 use crate::farcaster::{
     farcaster_client::FarcasterClient, AbortSwapRequest, CheckpointSelector, CheckpointsRequest,
-    InfoResponse, ListOffersRequest, MakeRequest, NeedsFundingRequest, OfferInfoRequest,
-    OfferSelector, PeersRequest, ProgressRequest, RestoreCheckpointRequest, RevokeOfferRequest,
-    SwapInfoRequest, SweepAddressRequest, TakeRequest,
+    InfoResponse, ListOffersRequest, MakeRequest, NeedsFundingRequest, NetworkSelector,
+    OfferInfoRequest, OfferSelector, PeersRequest, ProgressRequest, RestoreCheckpointRequest,
+    RevokeOfferRequest, SwapInfoRequest, SweepAddressRequest, TakeRequest,
 };
 use bitcoincore_rpc::RpcApi;
 use farcaster::{InfoRequest, MakeResponse, NeedsFundingResponse};
@@ -58,7 +58,8 @@ async fn grpc_server_functional_test() {
     // Test list checkpoints
     let request = tonic::Request::new(CheckpointsRequest {
         id: 2,
-        selector: CheckpointSelector::AllCheckpoints.into(),
+        checkpoint_selector: CheckpointSelector::AllCheckpoints.into(),
+        network_selector: NetworkSelector::AllNetworks.into(),
     });
     let response = farcaster_client_1.checkpoints(request).await;
     assert_eq!(response.unwrap().into_inner().id, 2);
@@ -121,7 +122,8 @@ async fn grpc_server_functional_test() {
     // Test List offers
     let list_offers_request = tonic::Request::new(ListOffersRequest {
         id: 22,
-        selector: OfferSelector::All.into(),
+        offer_selector: OfferSelector::All.into(),
+        network_selector: NetworkSelector::AllNetworks.into(),
     });
     let response = farcaster_client_1.list_offers(list_offers_request).await;
     assert_eq!(response.unwrap().into_inner().id, 22);
@@ -247,6 +249,7 @@ async fn retry_until_bitcoin_funding_info(
         let request = tonic::Request::new(NeedsFundingRequest {
             id: 11,
             blockchain: farcaster::Blockchain::Bitcoin.into(),
+            network_selector: NetworkSelector::LocalNetworks.into(),
         });
         let response = client.needs_funding(request).await;
         let NeedsFundingResponse { funding_infos, .. } = response.unwrap().into_inner();