Repository: farcaster-project/farcaster-node
Pull request #778 (2022-11-16 09:57:08)

Grpc: Support list offers endpoint
This allows the grpc endpoint to make use of the list offers and their status selector.


Patch:
diff --git a/src/grpcd/proto/farcaster.proto b/src/grpcd/proto/farcaster.proto
index 39ca47db..13a1afe6 100644
--- a/src/grpcd/proto/farcaster.proto
+++ b/src/grpcd/proto/farcaster.proto
@@ -15,6 +15,7 @@ service Farcaster {
     rpc Progress(ProgressRequest) returns (ProgressResponse){}
     rpc NeedsFunding(NeedsFundingRequest) returns (NeedsFundingResponse){}
     rpc SweepAddress(SweepAddressRequest) returns (SweepAddressResponse){}
+    rpc ListOffers(ListOffersRequest) returns (ListOffersResponse){}
 }
 
 message InfoRequest {
@@ -74,6 +75,23 @@ message PeersResponse {
     repeated string peers = 2;
 }
 
+message ListOffersRequest {
+    uint32 id = 1;
+    OfferSelector selector = 2;
+}
+
+enum OfferSelector {
+    OPEN = 0;
+    IN_PROGRESS = 1;
+    ENDED = 2;
+    ALL = 3;
+}
+
+message ListOffersResponse {
+    uint32 id = 1;
+    repeated string public_offers = 2;
+}
+
 message CheckpointsRequest {
     uint32 id = 1;
 }
diff --git a/src/grpcd/runtime.rs b/src/grpcd/runtime.rs
index 99d39e96..c8468093 100644
--- a/src/grpcd/runtime.rs
+++ b/src/grpcd/runtime.rs
@@ -2,6 +2,7 @@ use crate::bus::bridge::BridgeMsg;
 use crate::bus::ctl::ProtoPublicOffer;
 use crate::bus::ctl::PubOffer;
 use crate::bus::info::Address;
+use crate::bus::info::OfferStatusSelector;
 use crate::bus::AddressSecretKey;
 use crate::bus::Failure;
 use crate::service::Endpoints;
@@ -49,6 +50,8 @@ use self::farcaster::AbortSwapRequest;
 use self::farcaster::AbortSwapResponse;
 use self::farcaster::CheckpointsRequest;
 use self::farcaster::CheckpointsResponse;
+use self::farcaster::ListOffersRequest;
+use self::farcaster::ListOffersResponse;
 use self::farcaster::MakeRequest;
 use self::farcaster::MakeResponse;
 use self::farcaster::NeedsFundingRequest;
@@ -139,6 +142,17 @@ impl From<Blockchain> for farcaster::Blockchain {
     }
 }
 
+impl From<farcaster::OfferSelector> for OfferStatusSelector {
+    fn from(t: farcaster::OfferSelector) -> OfferStatusSelector {
+        match t {
+            farcaster::OfferSelector::All => OfferStatusSelector::All,
+            farcaster::OfferSelector::Open => OfferStatusSelector::Open,
+            farcaster::OfferSelector::InProgress => OfferStatusSelector::InProgress,
+            farcaster::OfferSelector::Ended => OfferStatusSelector::Ended,
+        }
+    }
+}
+
 #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, PartialOrd, Hash, Display)]
 #[display(Debug)]
 pub struct IdCounter(u64);
@@ -308,6 +322,45 @@ impl Farcaster for FarcasterService {
         }
     }
 
+    async fn list_offers(
+        &self,
+        request: GrpcRequest<ListOffersRequest>,
+    ) -> Result<GrpcResponse<ListOffersResponse>, Status> {
+        debug!("Received a grpc request: {:?}", request);
+        let ListOffersRequest {
+            id,
+            selector: grpc_offer_selector,
+        } = request.into_inner();
+        let selector = farcaster::OfferSelector::from_i32(grpc_offer_selector)
+            .ok_or(Status::invalid_argument("selector"))?
+            .into();
+        let oneshot_rx = self
+            .process_request(BusMsg::Bridge(BridgeMsg::Info {
+                request: InfoMsg::ListOffers(selector),
+                service_id: ServiceId::Farcasterd,
+            }))
+            .await?;
+        match oneshot_rx.await {
+            Ok(BusMsg::Info(InfoMsg::OfferList(mut offers))) => {
+                let reply = ListOffersResponse {
+                    id,
+                    public_offers: offers.drain(..).map(|o| o.offer).collect(),
+                };
+                Ok(GrpcResponse::new(reply))
+            }
+            Ok(BusMsg::Info(InfoMsg::OfferStatusList(mut offers))) => {
+                let reply = ListOffersResponse {
+                    id,
+                    public_offers: offers.drain(..).map(|o| o.offer.to_string()).collect(),
+                };
+                Ok(GrpcResponse::new(reply))
+            }
+            Err(error) => Err(Status::internal(format!("{}", error))),
+            Ok(BusMsg::Ctl(CtlMsg::Failure(Failure { info, .. }))) => Err(Status::internal(info)),
+            _ => Err(Status::invalid_argument("received invalid response")),
+        }
+    }
+
     async fn offer_info(
         &self,
         request: GrpcRequest<OfferInfoRequest>,


Test patch:
diff --git a/tests/grpc.rs b/tests/grpc.rs
index 3f46c148..d7aba032 100644
--- a/tests/grpc.rs
+++ b/tests/grpc.rs
@@ -3,9 +3,9 @@ extern crate log;
 
 use crate::farcaster::{
     farcaster_client::FarcasterClient, AbortSwapRequest, CheckpointsRequest, InfoResponse,
-    MakeRequest, NeedsFundingRequest, OfferInfoRequest, PeersRequest, ProgressRequest,
-    RestoreCheckpointRequest, RevokeOfferRequest, SwapInfoRequest, SweepAddressRequest,
-    TakeRequest,
+    ListOffersRequest, MakeRequest, NeedsFundingRequest, OfferInfoRequest, OfferSelector,
+    PeersRequest, ProgressRequest, RestoreCheckpointRequest, RevokeOfferRequest, SwapInfoRequest,
+    SweepAddressRequest, TakeRequest,
 };
 use bitcoincore_rpc::RpcApi;
 use farcaster::{InfoRequest, MakeResponse, NeedsFundingResponse};
@@ -111,6 +111,14 @@ async fn grpc_server_functional_test() {
     let response = farcaster_client_2.offer_info(offer_info_request).await;
     assert_eq!(response.unwrap().into_inner().id, 21);
 
+    // Test List offers
+    let list_offers_request = tonic::Request::new(ListOffersRequest {
+        id: 22,
+        selector: OfferSelector::All.into(),
+    });
+    let response = farcaster_client_1.list_offers(list_offers_request).await;
+    assert_eq!(response.unwrap().into_inner().id, 22);
+
     // Test take offer
     let take_request = TakeRequest {
         id: 5,