fedimint_server/consensus/aleph_bft/
keychain.rs

1use 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    // Tagging messages with the hash of the public key set ensures that peers with
35    // an incorrect public key set cannot create signatures that are accepted by
36    // their peers.
37    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}