1use std::{
3 collections::{HashMap, hash_map::Entry},
4 sync::Arc,
5};
6
7use anyhow::anyhow;
8
9use crate::{
10 MembershipDigest, RegisterError, Stake, StmResult,
11 membership_commitment::{MerkleTree, MerkleTreeConcatenationLeaf},
12 signature_scheme::{BlsVerificationKey, BlsVerificationKeyProofOfPossession},
13};
14
15pub type RegisteredParty = MerkleTreeConcatenationLeaf;
17
18#[derive(Clone, Debug, Default, PartialEq, Eq)]
22pub struct KeyRegistration {
23 keys: HashMap<BlsVerificationKey, Stake>,
24}
25
26impl KeyRegistration {
27 pub fn init() -> Self {
29 Self::default()
30 }
31
32 pub fn register(
36 &mut self,
37 stake: Stake,
38 pk: BlsVerificationKeyProofOfPossession,
39 ) -> StmResult<()> {
40 if let Entry::Vacant(e) = self.keys.entry(pk.vk) {
41 pk.verify_proof_of_possession()
42 .map_err(|_| RegisterError::KeyInvalid(Box::new(pk)))?;
43 e.insert(stake);
44 return Ok(());
45 }
46 Err(anyhow!(RegisterError::KeyRegistered(Box::new(pk.vk))))
47 }
48
49 pub fn close<D>(self) -> ClosedKeyRegistration<D>
52 where
53 D: MembershipDigest,
54 {
55 let mut total_stake: Stake = 0;
56 let mut reg_parties = self
57 .keys
58 .iter()
59 .map(|(&vk, &stake)| {
60 let (res, overflow) = total_stake.overflowing_add(stake);
61 if overflow {
62 panic!("Total stake overflow");
63 }
64 total_stake = res;
65 MerkleTreeConcatenationLeaf(vk, stake)
66 })
67 .collect::<Vec<RegisteredParty>>();
68 reg_parties.sort();
69
70 ClosedKeyRegistration {
71 merkle_tree: Arc::new(MerkleTree::new(®_parties)),
72 reg_parties,
73 total_stake,
74 }
75 }
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
81pub struct ClosedKeyRegistration<D: MembershipDigest> {
82 pub reg_parties: Vec<RegisteredParty>,
84 pub total_stake: Stake,
86 pub merkle_tree: Arc<MerkleTree<D::ConcatenationHash, MerkleTreeConcatenationLeaf>>,
88}
89
90#[cfg(test)]
91mod tests {
92 use proptest::{collection::vec, prelude::*};
93 use rand_chacha::ChaCha20Rng;
94 use rand_core::SeedableRng;
95
96 use crate::{MithrilMembershipDigest, signature_scheme::BlsSigningKey};
97
98 use super::*;
99
100 proptest! {
101 #[test]
102 fn test_keyreg(stake in vec(1..1u64 << 60, 2..=10),
103 nkeys in 2..10_usize,
104 fake_it in 0..4usize,
105 seed in any::<[u8;32]>()) {
106 let mut rng = ChaCha20Rng::from_seed(seed);
107 let mut kr = KeyRegistration::init();
108
109 let gen_keys = (1..nkeys).map(|_| {
110 let sk = BlsSigningKey::generate(&mut rng);
111 BlsVerificationKeyProofOfPossession::from(&sk)
112 }).collect::<Vec<_>>();
113
114 let fake_key = {
115 let sk = BlsSigningKey::generate(&mut rng);
116 BlsVerificationKeyProofOfPossession::from(&sk)
117 };
118
119 let mut keys = HashMap::new();
121
122 for (i, &stake) in stake.iter().enumerate() {
123 let mut pk = gen_keys[i % gen_keys.len()];
124
125 if fake_it == 0 {
126 pk.pop = fake_key.pop;
127 }
128
129 let reg = kr.register(stake, pk);
130
131 match reg {
132 Ok(_) => {
133 assert!(keys.insert(pk.vk, stake).is_none());
134 },
135 Err(error) => match error.downcast_ref::<RegisterError>(){
136 Some(RegisterError::KeyRegistered(pk1)) => {
137 assert!(pk1.as_ref() == &pk.vk);
138 assert!(keys.contains_key(&pk.vk));
139 },
140 Some(RegisterError::KeyInvalid(a)) => {
141 assert_eq!(fake_it, 0);
142 assert!(a.verify_proof_of_possession().is_err());
143 },
144 _ => {panic!("Unexpected error: {error}")}
145 }
146 }
147 }
148 if !kr.keys.is_empty() {
149 let closed = kr.close::<MithrilMembershipDigest>();
150 let retrieved_keys = closed.reg_parties.iter().map(|r| (r.0, r.1)).collect::<HashMap<_,_>>();
151 assert!(retrieved_keys == keys);
152 }
153 }
154 }
155
156 mod golden {
157 use blake2::{Blake2b, digest::consts::U32};
158
159 use crate::{
160 Initializer, MithrilMembershipDigest, Parameters,
161 membership_commitment::MerkleTreeBatchCommitment,
162 };
163
164 use super::*;
165
166 const GOLDEN_JSON: &str = r#"
167 {
168 "root":[4,3,108,183,145,65,166,69,250,202,51,64,90,232,45,103,56,138,102,63,209,245,81,22,120,16,6,96,140,204,210,55],
169 "nr_leaves":4,
170 "hasher":null
171 }"#;
172
173 fn golden_value() -> MerkleTreeBatchCommitment<Blake2b<U32>, MerkleTreeConcatenationLeaf> {
174 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
175 let params = Parameters {
176 m: 10,
177 k: 5,
178 phi_f: 0.8,
179 };
180 let number_of_parties = 4;
181
182 let mut key_reg = KeyRegistration::init();
183 for stake in 0..number_of_parties {
184 let initializer = Initializer::new(params, stake, &mut rng);
185 key_reg.register(initializer.stake, initializer.pk).unwrap();
186 }
187
188 let closed_key_reg: ClosedKeyRegistration<MithrilMembershipDigest> = key_reg.close();
189 closed_key_reg.merkle_tree.to_merkle_tree_batch_commitment()
190 }
191
192 #[test]
193 fn golden_conversions() {
194 let value = serde_json::from_str(GOLDEN_JSON)
195 .expect("This JSON deserialization should not fail");
196 assert_eq!(golden_value(), value);
197
198 let serialized =
199 serde_json::to_string(&value).expect("This JSON serialization should not fail");
200 let golden_serialized = serde_json::to_string(&golden_value())
201 .expect("This JSON serialization should not fail");
202 assert_eq!(golden_serialized, serialized);
203 }
204 }
205}