Skip to main content

fedimint_mint_client/
api.rs

1use fedimint_api_client::api::{
2    FederationApiExt, FederationResult, IModuleFederationApi, ServerResult,
3};
4use fedimint_core::bitcoin::hashes::sha256;
5use fedimint_core::module::registry::ModuleRegistry;
6use fedimint_core::module::{ApiRequestErased, SerdeModuleEncodingBase64};
7use fedimint_core::task::{MaybeSend, MaybeSync};
8use fedimint_core::{OutPoint, PeerId, apply, async_trait_maybe_send};
9use fedimint_mint_common::endpoint_constants::{
10    BLIND_NONCE_USED_ENDPOINT, NOTE_SPENT_ENDPOINT, RECOVERY_BLIND_NONCE_OUTPOINTS_ENDPOINT,
11    RECOVERY_COUNT_ENDPOINT, RECOVERY_SLICE_ENDPOINT, RECOVERY_SLICE_HASH_ENDPOINT,
12};
13use fedimint_mint_common::{BlindNonce, Nonce, RecoveryItem};
14
15use crate::error::FetchRecoverySliceError;
16
17#[apply(async_trait_maybe_send!)]
18pub trait MintFederationApi {
19    async fn check_blind_nonce_used(&self, blind_nonce: BlindNonce) -> FederationResult<bool>;
20
21    async fn check_note_spent(&self, nonce: Nonce) -> FederationResult<bool>;
22
23    /// Asks a single peer if e-cash has already been issued for `blind_nonce`.
24    ///
25    /// Unlike [`MintFederationApi::check_blind_nonce_used`] this does not wait
26    /// for a threshold of peers to agree, which makes it possible to observe
27    /// disagreement between peers.
28    async fn check_blind_nonce_used_single_peer(
29        &self,
30        peer: PeerId,
31        blind_nonce: BlindNonce,
32    ) -> ServerResult<bool>;
33
34    /// Asks a single peer if the note identified by `nonce` was already spent.
35    ///
36    /// Unlike [`MintFederationApi::check_note_spent`] this does not wait for a
37    /// threshold of peers to agree, which makes it possible to observe
38    /// disagreement between peers.
39    async fn check_note_spent_single_peer(&self, peer: PeerId, nonce: Nonce) -> ServerResult<bool>;
40
41    /// Returns the total number of recovery items stored on the federation.
42    async fn fetch_recovery_count(&self) -> FederationResult<u64>;
43
44    /// Returns the consensus hash of recovery items in the range `[start,
45    /// end)`.
46    async fn fetch_recovery_slice_hash(&self, start: u64, end: u64) -> sha256::Hash;
47
48    /// Fetches recovery items in the range `[start, end)` from a specific peer.
49    async fn fetch_recovery_slice(
50        &self,
51        peer: PeerId,
52        start: u64,
53        end: u64,
54    ) -> Result<Vec<RecoveryItem>, FetchRecoverySliceError>;
55
56    /// Returns the outpoints where the given blind nonces were used.
57    async fn fetch_blind_nonce_outpoints(
58        &self,
59        blind_nonces: Vec<BlindNonce>,
60    ) -> FederationResult<Vec<OutPoint>>;
61}
62
63#[apply(async_trait_maybe_send!)]
64impl<T: ?Sized> MintFederationApi for T
65where
66    T: IModuleFederationApi + MaybeSend + MaybeSync + 'static,
67{
68    async fn check_blind_nonce_used(&self, blind_nonce: BlindNonce) -> FederationResult<bool> {
69        self.request_current_consensus(
70            BLIND_NONCE_USED_ENDPOINT.to_string(),
71            ApiRequestErased::new(blind_nonce),
72        )
73        .await
74    }
75
76    async fn check_note_spent(&self, nonce: Nonce) -> FederationResult<bool> {
77        self.request_current_consensus(
78            NOTE_SPENT_ENDPOINT.to_string(),
79            ApiRequestErased::new(nonce),
80        )
81        .await
82    }
83
84    async fn check_blind_nonce_used_single_peer(
85        &self,
86        peer: PeerId,
87        blind_nonce: BlindNonce,
88    ) -> ServerResult<bool> {
89        self.request_single_peer::<bool>(
90            BLIND_NONCE_USED_ENDPOINT.to_string(),
91            ApiRequestErased::new(blind_nonce),
92            peer,
93        )
94        .await
95    }
96
97    async fn check_note_spent_single_peer(&self, peer: PeerId, nonce: Nonce) -> ServerResult<bool> {
98        self.request_single_peer::<bool>(
99            NOTE_SPENT_ENDPOINT.to_string(),
100            ApiRequestErased::new(nonce),
101            peer,
102        )
103        .await
104    }
105
106    async fn fetch_recovery_count(&self) -> FederationResult<u64> {
107        self.request_current_consensus::<u64>(
108            RECOVERY_COUNT_ENDPOINT.to_string(),
109            ApiRequestErased::default(),
110        )
111        .await
112    }
113
114    async fn fetch_recovery_slice_hash(&self, start: u64, end: u64) -> sha256::Hash {
115        self.request_current_consensus_retry(
116            RECOVERY_SLICE_HASH_ENDPOINT.to_owned(),
117            ApiRequestErased::new((start, end)),
118        )
119        .await
120    }
121
122    async fn fetch_recovery_slice(
123        &self,
124        peer: PeerId,
125        start: u64,
126        end: u64,
127    ) -> Result<Vec<RecoveryItem>, FetchRecoverySliceError> {
128        let result = self
129            .request_single_peer::<SerdeModuleEncodingBase64<Vec<RecoveryItem>>>(
130                RECOVERY_SLICE_ENDPOINT.to_owned(),
131                ApiRequestErased::new((start, end)),
132                peer,
133            )
134            .await?;
135
136        Ok(result.try_into_inner(&ModuleRegistry::default())?)
137    }
138
139    async fn fetch_blind_nonce_outpoints(
140        &self,
141        blind_nonces: Vec<BlindNonce>,
142    ) -> FederationResult<Vec<OutPoint>> {
143        self.request_current_consensus::<Vec<OutPoint>>(
144            RECOVERY_BLIND_NONCE_OUTPOINTS_ENDPOINT.to_string(),
145            ApiRequestErased::new(blind_nonces),
146        )
147        .await
148    }
149}