fedimint_lnv2_client/
api.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use fedimint_api_client::api::{
4    FederationApiExt, FederationResult, IModuleFederationApi, PeerResult,
5};
6use fedimint_api_client::query::FilterMapThreshold;
7use fedimint_core::module::{ApiAuth, ApiRequestErased};
8use fedimint_core::task::{MaybeSend, MaybeSync};
9use fedimint_core::util::SafeUrl;
10use fedimint_core::{NumPeersExt, OutPoint, PeerId, apply, async_trait_maybe_send};
11use fedimint_lnv2_common::ContractId;
12use fedimint_lnv2_common::endpoint_constants::{
13    ADD_GATEWAY_ENDPOINT, AWAIT_INCOMING_CONTRACT_ENDPOINT, AWAIT_PREIMAGE_ENDPOINT,
14    CONSENSUS_BLOCK_COUNT_ENDPOINT, GATEWAYS_ENDPOINT, REMOVE_GATEWAY_ENDPOINT,
15};
16use rand::seq::SliceRandom;
17
18#[apply(async_trait_maybe_send!)]
19pub trait LightningFederationApi {
20    async fn consensus_block_count(&self) -> FederationResult<u64>;
21
22    async fn await_incoming_contract(
23        &self,
24        contract_id: &ContractId,
25        expiration: u64,
26    ) -> Option<OutPoint>;
27
28    async fn await_preimage(&self, contract_id: OutPoint, expiration: u64) -> Option<[u8; 32]>;
29
30    async fn gateways(&self) -> FederationResult<Vec<SafeUrl>>;
31
32    async fn gateways_from_peer(&self, peer: PeerId) -> PeerResult<Vec<SafeUrl>>;
33
34    async fn add_gateway(&self, auth: ApiAuth, gateway: SafeUrl) -> FederationResult<bool>;
35
36    async fn remove_gateway(&self, auth: ApiAuth, gateway: SafeUrl) -> FederationResult<bool>;
37}
38
39#[apply(async_trait_maybe_send!)]
40impl<T: ?Sized> LightningFederationApi for T
41where
42    T: IModuleFederationApi + MaybeSend + MaybeSync + 'static,
43{
44    async fn consensus_block_count(&self) -> FederationResult<u64> {
45        self.request_current_consensus(
46            CONSENSUS_BLOCK_COUNT_ENDPOINT.to_string(),
47            ApiRequestErased::new(()),
48        )
49        .await
50    }
51
52    async fn await_incoming_contract(
53        &self,
54        contract_id: &ContractId,
55        expiration: u64,
56    ) -> Option<OutPoint> {
57        self.request_current_consensus_retry::<Option<OutPoint>>(
58            AWAIT_INCOMING_CONTRACT_ENDPOINT.to_string(),
59            ApiRequestErased::new((contract_id, expiration)),
60        )
61        .await
62    }
63
64    async fn await_preimage(&self, outpoint: OutPoint, expiration: u64) -> Option<[u8; 32]> {
65        self.request_current_consensus_retry(
66            AWAIT_PREIMAGE_ENDPOINT.to_string(),
67            ApiRequestErased::new((outpoint, expiration)),
68        )
69        .await
70    }
71
72    async fn gateways(&self) -> FederationResult<Vec<SafeUrl>> {
73        let gateways: BTreeMap<PeerId, Vec<SafeUrl>> = self
74            .request_with_strategy(
75                FilterMapThreshold::new(
76                    |_, gateways| Ok(gateways),
77                    self.all_peers().to_num_peers(),
78                ),
79                GATEWAYS_ENDPOINT.to_string(),
80                ApiRequestErased::default(),
81            )
82            .await?;
83
84        let mut union = gateways
85            .values()
86            .flatten()
87            .cloned()
88            .collect::<BTreeSet<SafeUrl>>()
89            .into_iter()
90            .collect::<Vec<SafeUrl>>();
91
92        // Shuffling the gateways ensures that payments are distributed over the
93        // gateways evenly.
94        union.shuffle(&mut rand::thread_rng());
95
96        union.sort_by_cached_key(|r| {
97            gateways
98                .values()
99                .filter(|response| !response.contains(r))
100                .count()
101        });
102
103        Ok(union)
104    }
105
106    async fn gateways_from_peer(&self, peer: PeerId) -> PeerResult<Vec<SafeUrl>> {
107        let gateways = self
108            .request_single_peer::<Vec<SafeUrl>>(
109                GATEWAYS_ENDPOINT.to_string(),
110                ApiRequestErased::default(),
111                peer,
112            )
113            .await?;
114
115        Ok(gateways)
116    }
117
118    async fn add_gateway(&self, auth: ApiAuth, gateway: SafeUrl) -> FederationResult<bool> {
119        let is_new_entry: bool = self
120            .request_admin(ADD_GATEWAY_ENDPOINT, ApiRequestErased::new(gateway), auth)
121            .await?;
122
123        Ok(is_new_entry)
124    }
125
126    async fn remove_gateway(&self, auth: ApiAuth, gateway: SafeUrl) -> FederationResult<bool> {
127        let entry_existed: bool = self
128            .request_admin(
129                REMOVE_GATEWAY_ENDPOINT,
130                ApiRequestErased::new(gateway),
131                auth,
132            )
133            .await?;
134
135        Ok(entry_existed)
136    }
137}