fedimint_core/net/
api_announcement.rs1use std::collections::BTreeMap;
2
3use bitcoin::hashes::{sha256, Hash};
4use bitcoin::secp256k1::Message;
5use fedimint_core::db::DatabaseLookup;
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::task::MaybeSend;
8use fedimint_core::PeerId;
9use futures::StreamExt;
10use jsonrpsee_core::Serialize;
11use serde::Deserialize;
12
13use crate::db::{
14 Database, DatabaseKey, DatabaseKeyPrefix, DatabaseRecord, IDatabaseTransactionOpsCoreTyped,
15};
16use crate::task::MaybeSync;
17use crate::util::SafeUrl;
18
19const API_ANNOUNCEMENT_MESSAGE_TAG: &[u8] = b"fedimint-api-announcement";
20
21#[derive(Debug, Serialize, Deserialize, Clone, Eq, Hash, PartialEq, Encodable, Decodable)]
22pub struct ApiAnnouncement {
23 pub api_url: SafeUrl,
24 pub nonce: u64,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone, Eq, Hash, PartialEq, Encodable, Decodable)]
28pub struct SignedApiAnnouncement {
29 pub api_announcement: ApiAnnouncement,
30 pub signature: secp256k1::schnorr::Signature,
31}
32
33#[derive(Debug, Serialize, Deserialize, Clone, Eq, Hash, PartialEq, Encodable, Decodable)]
34pub struct SignedApiAnnouncementSubmission {
35 #[serde(flatten)]
36 pub signed_api_announcement: SignedApiAnnouncement,
37 pub peer_id: PeerId,
38}
39
40impl ApiAnnouncement {
41 pub fn new(api_url: SafeUrl, nonce: u64) -> Self {
42 Self { api_url, nonce }
43 }
44
45 pub fn tagged_hash(&self) -> sha256::Hash {
46 let mut msg = API_ANNOUNCEMENT_MESSAGE_TAG.to_vec();
47 self.consensus_encode(&mut msg)
48 .expect("writing to vec is infallible");
49 sha256::Hash::hash(&msg)
50 }
51
52 pub fn sign<C: secp256k1::Signing>(
53 &self,
54 ctx: &secp256k1::Secp256k1<C>,
55 key: &secp256k1::Keypair,
56 ) -> SignedApiAnnouncement {
57 let msg = Message::from_digest(*self.tagged_hash().as_ref());
58 let signature = ctx.sign_schnorr(&msg, key);
59 SignedApiAnnouncement {
60 api_announcement: self.clone(),
61 signature,
62 }
63 }
64}
65
66impl SignedApiAnnouncement {
67 pub fn verify<C: secp256k1::Verification>(
69 &self,
70 ctx: &secp256k1::Secp256k1<C>,
71 pk: &secp256k1::PublicKey,
72 ) -> bool {
73 let msg = Message::from_digest(*self.api_announcement.tagged_hash().as_ref());
74 ctx.verify_schnorr(&self.signature, &msg, &pk.x_only_public_key().0)
75 .is_ok()
76 }
77}
78
79pub async fn override_api_urls<P>(
85 db: &Database,
86 cfg_api_urls: impl IntoIterator<Item = (PeerId, SafeUrl)>,
87 db_key_prefix: &P,
88 key_to_peer_id: impl Fn(&P::Record) -> PeerId,
89) -> BTreeMap<PeerId, SafeUrl>
90where
91 P: DatabaseLookup + DatabaseKeyPrefix + MaybeSend + MaybeSync,
92 P::Record: DatabaseRecord<Value = SignedApiAnnouncement> + DatabaseKey + MaybeSend + MaybeSync,
93{
94 let mut db_api_urls = db
95 .begin_transaction_nc()
96 .await
97 .find_by_prefix(db_key_prefix)
98 .await
99 .map(|(key, announcement)| (key_to_peer_id(&key), announcement.api_announcement.api_url))
100 .collect::<BTreeMap<_, _>>()
101 .await;
102
103 cfg_api_urls
104 .into_iter()
105 .map(|(peer_id, cfg_api_url)| {
106 (peer_id, db_api_urls.remove(&peer_id).unwrap_or(cfg_api_url))
107 })
108 .collect::<BTreeMap<_, _>>()
109}