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