fedimint_mintv2_client/
api.rs1use std::collections::BTreeMap;
2use std::time::Duration;
3
4use bitcoin_hashes::sha256;
5use fedimint_api_client::api::{DynModuleApi, FederationApiExt, ServerError};
6use fedimint_api_client::query::FilterMapThreshold;
7use fedimint_core::module::ApiRequestErased;
8use fedimint_core::{NumPeersExt, OutPointRange, PeerId};
9use fedimint_mintv2_common::endpoint_constants::{
10 RECOVERY_COUNT_ENDPOINT, RECOVERY_SLICE_ENDPOINT, RECOVERY_SLICE_HASH_ENDPOINT,
11 SIGNATURE_SHARES_ENDPOINT, SIGNATURE_SHARES_RECOVERY_ENDPOINT,
12};
13use fedimint_mintv2_common::{Denomination, RecoveryItem};
14use tbs::{BlindedMessage, BlindedSignatureShare, PublicKeyShare};
15
16use crate::NoteIssuanceRequest;
17use crate::output::verify_blind_shares;
18
19#[async_trait::async_trait]
20pub trait MintV2ModuleApi {
21 async fn fetch_signature_shares(
22 &self,
23 range: OutPointRange,
24 issuance_requests: Vec<NoteIssuanceRequest>,
25 tbs_pks: BTreeMap<Denomination, BTreeMap<PeerId, PublicKeyShare>>,
26 ) -> BTreeMap<PeerId, Vec<BlindedSignatureShare>>;
27
28 async fn fetch_signature_shares_recovery(
29 &self,
30 issuance_requests: Vec<NoteIssuanceRequest>,
31 tbs_pks: BTreeMap<Denomination, BTreeMap<PeerId, PublicKeyShare>>,
32 ) -> BTreeMap<PeerId, Vec<BlindedSignatureShare>>;
33
34 async fn fetch_recovery_count(&self) -> anyhow::Result<u64>;
35
36 async fn fetch_recovery_slice_hash(&self, start: u64, end: u64) -> sha256::Hash;
37
38 async fn fetch_recovery_slice(
39 &self,
40 peer: PeerId,
41 timeout: Duration,
42 start: u64,
43 end: u64,
44 ) -> anyhow::Result<Vec<RecoveryItem>>;
45}
46
47#[async_trait::async_trait]
48impl MintV2ModuleApi for DynModuleApi {
49 async fn fetch_signature_shares(
50 &self,
51 range: OutPointRange,
52 issuance_requests: Vec<NoteIssuanceRequest>,
53 tbs_pks: BTreeMap<Denomination, BTreeMap<PeerId, PublicKeyShare>>,
54 ) -> BTreeMap<PeerId, Vec<BlindedSignatureShare>> {
55 self.request_with_strategy_retry(
56 FilterMapThreshold::new(
58 move |peer, signature_shares| {
59 verify_blind_shares(peer, signature_shares, &issuance_requests, &tbs_pks)
60 .map_err(ServerError::InvalidResponse)
61 },
62 self.all_peers().to_num_peers(),
63 ),
64 SIGNATURE_SHARES_ENDPOINT.to_owned(),
65 ApiRequestErased::new(range),
66 )
67 .await
68 }
69
70 async fn fetch_signature_shares_recovery(
71 &self,
72 issuance_requests: Vec<NoteIssuanceRequest>,
73 tbs_pks: BTreeMap<Denomination, BTreeMap<PeerId, PublicKeyShare>>,
74 ) -> BTreeMap<PeerId, Vec<BlindedSignatureShare>> {
75 let blinded_messages: Vec<BlindedMessage> = issuance_requests
76 .iter()
77 .map(NoteIssuanceRequest::blinded_message)
78 .collect();
79
80 self.request_with_strategy_retry(
81 FilterMapThreshold::new(
83 move |peer, signature_shares| {
84 verify_blind_shares(peer, signature_shares, &issuance_requests, &tbs_pks)
85 .map_err(ServerError::InvalidResponse)
86 },
87 self.all_peers().to_num_peers(),
88 ),
89 SIGNATURE_SHARES_RECOVERY_ENDPOINT.to_owned(),
90 ApiRequestErased::new(blinded_messages),
91 )
92 .await
93 }
94
95 async fn fetch_recovery_count(&self) -> anyhow::Result<u64> {
96 self.request_current_consensus::<u64>(
97 RECOVERY_COUNT_ENDPOINT.to_string(),
98 ApiRequestErased::default(),
99 )
100 .await
101 .map_err(|e| anyhow::anyhow!("{}", e))
102 }
103
104 async fn fetch_recovery_slice_hash(&self, start: u64, end: u64) -> sha256::Hash {
105 self.request_current_consensus_retry(
106 RECOVERY_SLICE_HASH_ENDPOINT.to_owned(),
107 ApiRequestErased::new((start, end)),
108 )
109 .await
110 }
111
112 async fn fetch_recovery_slice(
113 &self,
114 peer: PeerId,
115 timeout: Duration,
116 start: u64,
117 end: u64,
118 ) -> anyhow::Result<Vec<RecoveryItem>> {
119 let result = tokio::time::timeout(
120 timeout,
121 self.request_single_peer::<Vec<RecoveryItem>>(
122 RECOVERY_SLICE_ENDPOINT.to_owned(),
123 ApiRequestErased::new((start, end)),
124 peer,
125 ),
126 )
127 .await??;
128
129 Ok(result)
130 }
131}