fedimint_server/consensus/aleph_bft/
keychain.rs1use std::collections::BTreeMap;
2use std::io::Write;
3
4use aleph_bft::Keychain as KeychainTrait;
5use bitcoin::hashes::Hash;
6use fedimint_core::encoding::Encodable;
7use fedimint_core::session_outcome::SchnorrSignature;
8use fedimint_core::{NumPeersExt, PeerId, secp256k1};
9use secp256k1::hashes::sha256;
10use secp256k1::{Keypair, Message, PublicKey, schnorr};
11
12use crate::config::ServerConfig;
13
14#[derive(Clone, Debug)]
15pub struct Keychain {
16 identity: PeerId,
17 pks: BTreeMap<PeerId, PublicKey>,
18 message_tag: sha256::Hash,
19 keypair: Keypair,
20}
21
22impl Keychain {
23 pub fn new(cfg: &ServerConfig) -> Self {
24 Keychain {
25 identity: cfg.local.identity,
26 pks: cfg.consensus.broadcast_public_keys.clone(),
27 message_tag: cfg.consensus.broadcast_public_keys.consensus_hash(),
28 keypair: cfg
29 .private
30 .broadcast_secret_key
31 .keypair(secp256k1::SECP256K1),
32 }
33 }
34
35 fn tagged_message(&self, message: &[u8]) -> Message {
39 let mut engine = sha256::HashEngine::default();
40
41 engine
42 .write_all(self.message_tag.as_ref())
43 .expect("Writing to a hash engine can not fail");
44
45 engine
46 .write_all(message)
47 .expect("Writing to a hash engine can not fail");
48
49 let hash = sha256::Hash::from_engine(engine);
50
51 Message::from_digest(*hash.as_ref())
52 }
53}
54
55impl aleph_bft::Index for Keychain {
56 fn index(&self) -> aleph_bft::NodeIndex {
57 self.identity.to_usize().into()
58 }
59}
60
61#[async_trait::async_trait]
62impl aleph_bft::Keychain for Keychain {
63 type Signature = SchnorrSignature;
64
65 fn node_count(&self) -> aleph_bft::NodeCount {
66 self.pks.len().into()
67 }
68
69 fn sign(&self, message: &[u8]) -> Self::Signature {
70 SchnorrSignature(
71 self.keypair
72 .sign_schnorr(self.tagged_message(message))
73 .as_ref()
74 .to_owned(),
75 )
76 }
77
78 fn verify(
79 &self,
80 message: &[u8],
81 signature: &Self::Signature,
82 node_index: aleph_bft::NodeIndex,
83 ) -> bool {
84 if let Some(public_key) = self.pks.get(&super::to_peer_id(node_index)) {
85 if let Ok(sig) = schnorr::Signature::from_slice(&signature.0) {
86 return secp256k1::SECP256K1
87 .verify_schnorr(
88 &sig,
89 &self.tagged_message(message),
90 &public_key.x_only_public_key().0,
91 )
92 .is_ok();
93 }
94 }
95
96 false
97 }
98}
99
100impl aleph_bft::MultiKeychain for Keychain {
101 type PartialMultisignature = aleph_bft::NodeMap<SchnorrSignature>;
102
103 fn bootstrap_multi(
104 &self,
105 signature: &Self::Signature,
106 index: aleph_bft::NodeIndex,
107 ) -> Self::PartialMultisignature {
108 let mut partial = aleph_bft::NodeMap::with_size(self.pks.len().into());
109 partial.insert(index, signature.clone());
110 partial
111 }
112
113 fn is_complete(&self, msg: &[u8], partial: &Self::PartialMultisignature) -> bool {
114 if partial.iter().count() < self.pks.to_num_peers().threshold() {
115 return false;
116 }
117
118 partial.iter().all(|(i, sgn)| self.verify(msg, sgn, i))
119 }
120}