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