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 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}