Skip to content

Commit 2069a5f

Browse files
committed
Add a SignerRegisterer module in Aggregator
1 parent d1c3eeb commit 2069a5f

File tree

3 files changed

+306
-1
lines changed

3 files changed

+306
-1
lines changed

mithril-aggregator/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod dependency;
1818
mod http_server;
1919
mod multi_signer;
2020
mod runtime;
21+
mod signer_registerer;
2122
mod snapshot_stores;
2223
mod snapshot_uploaders;
2324
mod snapshotter;
@@ -35,6 +36,10 @@ pub use command_args::MainOpts;
3536
pub use dependency::DependencyManager;
3637
pub use http_server::Server;
3738
pub use runtime::{AggregatorConfig, AggregatorRunner, AggregatorRunnerTrait, AggregatorRuntime};
39+
pub use signer_registerer::{
40+
MithrilSignerRegisterer, SignerRegisterer, SignerRegistrationError, SignerRegistrationRound,
41+
SignerRegistrationRoundOpener,
42+
};
3843
pub use snapshot_uploaders::{
3944
DumbSnapshotUploader, LocalSnapshotUploader, RemoteSnapshotUploader, SnapshotUploader,
4045
};
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
use async_trait::async_trait;
2+
use std::sync::Arc;
3+
use thiserror::Error;
4+
use tokio::sync::RwLock;
5+
6+
use crate::{VerificationKeyStore, VerificationKeyStorer};
7+
use mithril_common::{
8+
chain_observer::ChainObserver,
9+
crypto_helper::{
10+
key_decode_hex, KESPeriod, ProtocolKeyRegistration, ProtocolRegistrationError,
11+
},
12+
entities::{Epoch, Signer, StakeDistribution},
13+
store::StoreError,
14+
};
15+
16+
#[cfg(test)]
17+
use mockall::automock;
18+
19+
/// Error type for signer registerer service.
20+
#[derive(Error, Debug)]
21+
pub enum SignerRegistrationError {
22+
/// No signer registration round opened yet
23+
#[error("a signer registration round has not yet to be opened")]
24+
RegistrationRoundNotYetOpened,
25+
26+
/// Codec error.
27+
#[error("codec error: '{0}'")]
28+
Codec(String),
29+
30+
/// Chain observer error.
31+
#[error("chaim observer error: '{0}'")]
32+
ChainObserver(String),
33+
34+
/// Signer is already registered.
35+
#[error("signer already registered")]
36+
ExistingSigner(),
37+
38+
/// Store error.
39+
#[error("store error: {0}")]
40+
StoreError(#[from] StoreError),
41+
42+
/// Signer registration failed.
43+
#[error("signer registration failed")]
44+
FailedSignerRegistration(#[from] ProtocolRegistrationError),
45+
}
46+
47+
/// Represents the information needed to handle a signer registration round
48+
pub struct SignerRegistrationRound {
49+
epoch: Epoch,
50+
stake_distribution: StakeDistribution,
51+
}
52+
53+
/// Trait to register a signer
54+
#[async_trait]
55+
#[cfg_attr(test, automock)]
56+
pub trait SignerRegisterer: Sync + Send {
57+
/// Register a signer
58+
async fn register_signer(&self, signer: &Signer) -> Result<(), SignerRegistrationError>;
59+
}
60+
61+
// Trait to open a signer registration round
62+
#[async_trait]
63+
#[cfg_attr(test, automock)]
64+
pub trait SignerRegistrationRoundOpener: Sync + Send {
65+
/// Open a signer registration round
66+
async fn open_registration_round(
67+
&self,
68+
registration_epoch: Epoch,
69+
stake_distribution: StakeDistribution,
70+
) -> Result<(), SignerRegistrationError>;
71+
}
72+
73+
/// Implementation of a [SignerRegisterer]
74+
pub struct MithrilSignerRegisterer {
75+
/// Current signer registration round
76+
current_round: RwLock<Option<SignerRegistrationRound>>,
77+
78+
/// Chain observer service.
79+
chain_observer: Arc<dyn ChainObserver>,
80+
81+
/// Verification key store
82+
verification_key_store: Arc<VerificationKeyStore>,
83+
}
84+
85+
impl MithrilSignerRegisterer {
86+
/// MithrilSignerRegisterer factory
87+
pub fn new(
88+
chain_observer: Arc<dyn ChainObserver>,
89+
verification_key_store: Arc<VerificationKeyStore>,
90+
) -> Self {
91+
Self {
92+
current_round: RwLock::new(None),
93+
chain_observer,
94+
verification_key_store,
95+
}
96+
}
97+
}
98+
99+
#[async_trait]
100+
impl SignerRegistrationRoundOpener for MithrilSignerRegisterer {
101+
async fn open_registration_round(
102+
&self,
103+
registration_epoch: Epoch,
104+
stake_distribution: StakeDistribution,
105+
) -> Result<(), SignerRegistrationError> {
106+
let mut current_round = self.current_round.write().await;
107+
*current_round = Some(SignerRegistrationRound {
108+
epoch: registration_epoch,
109+
stake_distribution,
110+
});
111+
112+
Ok(())
113+
}
114+
}
115+
116+
#[async_trait]
117+
impl SignerRegisterer for MithrilSignerRegisterer {
118+
async fn register_signer(&self, signer: &Signer) -> Result<(), SignerRegistrationError> {
119+
let registration_round = self.current_round.read().await;
120+
let registration_round = registration_round
121+
.as_ref()
122+
.ok_or(SignerRegistrationError::RegistrationRoundNotYetOpened)?;
123+
124+
let mut key_registration = ProtocolKeyRegistration::init(
125+
&registration_round
126+
.stake_distribution
127+
.iter()
128+
.map(|(k, v)| (k.to_owned(), *v))
129+
.collect::<Vec<_>>(),
130+
);
131+
let party_id_register = match signer.party_id.as_str() {
132+
"" => None,
133+
party_id => Some(party_id.to_string()),
134+
};
135+
let verification_key =
136+
key_decode_hex(&signer.verification_key).map_err(SignerRegistrationError::Codec)?;
137+
let verification_key_signature = match &signer.verification_key_signature {
138+
Some(verification_key_signature) => Some(
139+
key_decode_hex(verification_key_signature)
140+
.map_err(SignerRegistrationError::Codec)?,
141+
),
142+
_ => None,
143+
};
144+
let operational_certificate = match &signer.operational_certificate {
145+
Some(operational_certificate) => Some(
146+
key_decode_hex(operational_certificate).map_err(SignerRegistrationError::Codec)?,
147+
),
148+
_ => None,
149+
};
150+
let kes_period = match &operational_certificate {
151+
Some(operational_certificate) => Some(
152+
self.chain_observer
153+
.get_current_kes_period(operational_certificate)
154+
.await
155+
.map_err(|e| SignerRegistrationError::ChainObserver(e.to_string()))?
156+
.unwrap_or_default()
157+
- operational_certificate.start_kes_period as KESPeriod,
158+
),
159+
None => None,
160+
};
161+
let party_id_save = key_registration.register(
162+
party_id_register.clone(),
163+
operational_certificate,
164+
verification_key_signature,
165+
kes_period,
166+
verification_key,
167+
)?;
168+
let mut signer_save = signer.to_owned();
169+
signer_save.party_id = party_id_save;
170+
171+
match self
172+
.verification_key_store
173+
.save_verification_key(registration_round.epoch, signer_save)
174+
.await?
175+
{
176+
Some(_) => Err(SignerRegistrationError::ExistingSigner()),
177+
None => Ok(()),
178+
}
179+
}
180+
}
181+
182+
#[cfg(test)]
183+
mod tests {
184+
use std::{collections::HashMap, sync::Arc};
185+
186+
use mithril_common::{
187+
chain_observer::FakeObserver,
188+
crypto_helper::tests_setup::setup_signers,
189+
entities::{Epoch, PartyId, Signer},
190+
fake_data,
191+
store::adapter::MemoryAdapter,
192+
};
193+
194+
use crate::{
195+
MithrilSignerRegisterer, SignerRegisterer, SignerRegistrationRoundOpener,
196+
VerificationKeyStore, VerificationKeyStorer,
197+
};
198+
199+
#[tokio::test]
200+
async fn can_register_signer_if_registration_round_is_opened_with_operational_certificate() {
201+
let chain_observer = FakeObserver::default();
202+
let verification_key_store = Arc::new(VerificationKeyStore::new(
203+
Box::new(MemoryAdapter::<Epoch, HashMap<PartyId, Signer>>::new(None).unwrap()),
204+
None,
205+
));
206+
let signer_registerer =
207+
MithrilSignerRegisterer::new(Arc::new(chain_observer), verification_key_store.clone());
208+
let registration_epoch = Epoch(1);
209+
let signers = setup_signers(5, &fake_data::protocol_parameters().into());
210+
let signer_to_register: Signer = signers[0].0.clone().into();
211+
assert!(signer_to_register.operational_certificate.is_some());
212+
let stake_distribution = signers
213+
.into_iter()
214+
.map(|s| (s.0.party_id, s.0.stake))
215+
.collect::<HashMap<_, _>>();
216+
217+
signer_registerer
218+
.open_registration_round(registration_epoch, stake_distribution)
219+
.await
220+
.expect("signer registration round opening should not fail");
221+
222+
signer_registerer
223+
.register_signer(&signer_to_register)
224+
.await
225+
.expect("signer registration should not fail");
226+
227+
let registered_signers = &verification_key_store
228+
.get_verification_keys(registration_epoch)
229+
.await
230+
.expect("registered signers retrieval should not fail");
231+
232+
assert_eq!(
233+
&Some(HashMap::from([(
234+
signer_to_register.party_id.clone(),
235+
signer_to_register
236+
)])),
237+
registered_signers
238+
);
239+
}
240+
241+
#[tokio::test]
242+
async fn can_register_signer_if_registration_round_is_opened_without_operational_certificate() {
243+
let chain_observer = FakeObserver::default();
244+
let verification_key_store = Arc::new(VerificationKeyStore::new(
245+
Box::new(MemoryAdapter::<Epoch, HashMap<PartyId, Signer>>::new(None).unwrap()),
246+
None,
247+
));
248+
let signer_registerer =
249+
MithrilSignerRegisterer::new(Arc::new(chain_observer), verification_key_store.clone());
250+
let registration_epoch = Epoch(1);
251+
let signers = setup_signers(5, &fake_data::protocol_parameters().into());
252+
let signer_to_register: Signer = signers[1].0.clone().into();
253+
assert!(signer_to_register.operational_certificate.is_none());
254+
let stake_distribution = signers
255+
.into_iter()
256+
.map(|s| (s.0.party_id, s.0.stake))
257+
.collect::<HashMap<_, _>>();
258+
259+
signer_registerer
260+
.open_registration_round(registration_epoch, stake_distribution)
261+
.await
262+
.expect("signer registration round opening should not fail");
263+
264+
signer_registerer
265+
.register_signer(&signer_to_register)
266+
.await
267+
.expect("signer registration should not fail");
268+
269+
let registered_signers = &verification_key_store
270+
.get_verification_keys(registration_epoch)
271+
.await
272+
.expect("registered signers retrieval should not fail");
273+
274+
assert_eq!(
275+
&Some(HashMap::from([(
276+
signer_to_register.party_id.clone(),
277+
signer_to_register
278+
)])),
279+
registered_signers
280+
);
281+
}
282+
283+
#[tokio::test]
284+
async fn cant_register_signer_if_registration_round_is_not_opened() {
285+
let chain_observer = FakeObserver::default();
286+
let verification_key_store = Arc::new(VerificationKeyStore::new(
287+
Box::new(MemoryAdapter::<Epoch, HashMap<PartyId, Signer>>::new(None).unwrap()),
288+
None,
289+
));
290+
let signer_registerer =
291+
MithrilSignerRegisterer::new(Arc::new(chain_observer), verification_key_store.clone());
292+
let signers = setup_signers(5, &fake_data::protocol_parameters().into());
293+
let signer_to_register: Signer = signers[1].0.clone().into();
294+
295+
signer_registerer
296+
.register_signer(&signer_to_register)
297+
.await
298+
.expect_err("signer registration should fail if no round opened");
299+
}
300+
}

mithril-common/src/crypto_helper/cardano/key_certification.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ impl KeyRegWrapper {
252252
if cfg!(not(feature = "allow_skip_signer_certification")) {
253253
Err(ProtocolRegistrationErrorWrapper::OpCertMissing)?
254254
}
255-
println!("WARNING: Uncertified signer regsitration by providing a Pool Id is deprecated and will be removed soon! (Pool Id: {:?})", party_id);
255+
println!("WARNING: Uncertified signer registration by providing a Pool Id is deprecated and will be removed soon! (Pool Id: {:?})", party_id);
256256
party_id.ok_or(ProtocolRegistrationErrorWrapper::PartyIdMissing)?
257257
};
258258

0 commit comments

Comments
 (0)