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