fedimint_core/net/
api_announcement.rs
1use std::collections::BTreeMap;
2
3use bitcoin::hashes::{Hash, sha256};
4use bitcoin::secp256k1::Message;
5use fedimint_core::PeerId;
6use fedimint_core::db::DatabaseLookup;
7use fedimint_core::encoding::{Decodable, Encodable};
8use fedimint_core::task::MaybeSend;
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 msg.append(&mut self.consensus_encode_to_vec());
48 sha256::Hash::hash(&msg)
49 }
50
51 pub fn sign<C: secp256k1::Signing>(
52 &self,
53 ctx: &secp256k1::Secp256k1<C>,
54 key: &secp256k1::Keypair,
55 ) -> SignedApiAnnouncement {
56 let msg = Message::from_digest(*self.tagged_hash().as_ref());
57 let signature = ctx.sign_schnorr(&msg, key);
58 SignedApiAnnouncement {
59 api_announcement: self.clone(),
60 signature,
61 }
62 }
63}
64
65impl SignedApiAnnouncement {
66 pub fn verify<C: secp256k1::Verification>(
68 &self,
69 ctx: &secp256k1::Secp256k1<C>,
70 pk: &secp256k1::PublicKey,
71 ) -> bool {
72 let msg = Message::from_digest(*self.api_announcement.tagged_hash().as_ref());
73 ctx.verify_schnorr(&self.signature, &msg, &pk.x_only_public_key().0)
74 .is_ok()
75 }
76}
77
78pub async fn override_api_urls<P>(
84 db: &Database,
85 cfg_api_urls: impl IntoIterator<Item = (PeerId, SafeUrl)>,
86 db_key_prefix: &P,
87 key_to_peer_id: impl Fn(&P::Record) -> PeerId,
88) -> BTreeMap<PeerId, SafeUrl>
89where
90 P: DatabaseLookup + DatabaseKeyPrefix + MaybeSend + MaybeSync,
91 P::Record: DatabaseRecord<Value = SignedApiAnnouncement> + DatabaseKey + MaybeSend + MaybeSync,
92{
93 let mut db_api_urls = db
94 .begin_transaction_nc()
95 .await
96 .find_by_prefix(db_key_prefix)
97 .await
98 .map(|(key, announcement)| (key_to_peer_id(&key), announcement.api_announcement.api_url))
99 .collect::<BTreeMap<_, _>>()
100 .await;
101
102 cfg_api_urls
103 .into_iter()
104 .map(|(peer_id, cfg_api_url)| {
105 (peer_id, db_api_urls.remove(&peer_id).unwrap_or(cfg_api_url))
106 })
107 .collect::<BTreeMap<_, _>>()
108}