diff --git a/demo/protocol-demo/src/demonstrator.rs b/demo/protocol-demo/src/demonstrator.rs index a5ad1d180d6..d5c2172d178 100644 --- a/demo/protocol-demo/src/demonstrator.rs +++ b/demo/protocol-demo/src/demonstrator.rs @@ -20,7 +20,7 @@ struct PlayerArtifact { party_id: ProtocolPartyId, stake: ProtocolStake, verification_key: String, - secret_key: String, + initializer: String, } /// Single Signature artifacts @@ -136,7 +136,6 @@ impl Party { signatures: &[(ProtocolSingleSignature, u64)], ) -> Option<&ProtocolMultiSignature> { let unzipped_signatures: (Vec<_>, Vec<_>) = signatures.iter().cloned().unzip(); - println!("123 {}", signatures.len()); let msig = self .clerk .as_ref() @@ -365,12 +364,11 @@ impl ProtocolDemonstrator for Demonstrator { ProtocolInitializer::setup(self.params.unwrap(), party_id, stake, &mut rng); let verification_key: ProtocolSignerVerificationKey = protocol_initializer.verification_key(); - let secret_key: ProtocolSignerSecretKey = protocol_initializer.secret_key(); players_artifacts.push(PlayerArtifact { party_id: protocol_initializer.party_id(), stake: protocol_initializer.stake(), verification_key: key_encode_hex(verification_key).unwrap(), - secret_key: key_encode_hex(secret_key).unwrap(), + initializer: key_encode_hex(protocol_initializer).unwrap(), }) } let players_with_keys = players_artifacts diff --git a/demo/protocol-demo/src/main.rs b/demo/protocol-demo/src/main.rs index cdedfef46e2..58626cbc039 100644 --- a/demo/protocol-demo/src/main.rs +++ b/demo/protocol-demo/src/main.rs @@ -7,7 +7,7 @@ use clap::Parser; #[derive(Parser, Debug, PartialEq, Clone, Copy)] pub struct Config { /// Security parameter, upper bound on indices - #[clap(short, long, default_value_t = 100)] + #[clap(short, long, default_value_t = 200)] m: u64, /// Quorum parameter diff --git a/mithril-aggregator/Cargo.lock b/mithril-aggregator/Cargo.lock index 52d49c022f3..e249564be3e 100644 --- a/mithril-aggregator/Cargo.lock +++ b/mithril-aggregator/Cargo.lock @@ -1632,6 +1632,7 @@ dependencies = [ "slog-bunyan", "slog-scope", "slog-term", + "thiserror", "tokio", "tokio-util 0.7.1", "warp", diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index a64100d0396..02736af1202 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -30,6 +30,7 @@ futures = "0.3" reqwest = { version = "0.11", features = ["json"] } config = "0.13.1" blake2 = "0.9.2" +thiserror = "1.0.31" [dev-dependencies] mockall = "0.11.0" diff --git a/mithril-aggregator/src/http_server.rs b/mithril-aggregator/src/http_server.rs index 69e37ce106b..800bb02d40a 100644 --- a/mithril-aggregator/src/http_server.rs +++ b/mithril-aggregator/src/http_server.rs @@ -1,5 +1,5 @@ use chrono::prelude::*; -use mithril_common::crypto_helper::{key_decode_hex, key_encode_hex}; +use mithril_common::crypto_helper::{key_decode_hex, key_encode_hex, ProtocolPartyId}; use mithril_common::entities; use mithril_common::fake_data; use serde_json::Value::Null; @@ -67,10 +67,11 @@ mod router { /// GET /certificate-pending pub fn certificate_pending( - _dependency_manager: Arc, + dependency_manager: Arc, ) -> impl Filter + Clone { warp::path!("certificate-pending") .and(warp::get()) + .and(with_multi_signer(dependency_manager)) .and_then(handlers::certificate_pending) } @@ -106,11 +107,12 @@ mod router { /// POST /register-signer pub fn register_signer( - _dependency_manager: Arc, + dependency_manager: Arc, ) -> impl Filter + Clone { warp::path!("register-signer") .and(warp::post()) .and(warp::body::json()) + .and(with_multi_signer(dependency_manager)) .and_then(handlers::register_signer) } @@ -144,13 +146,49 @@ mod handlers { use super::*; /// Certificate Pending - pub async fn certificate_pending() -> Result { + pub async fn certificate_pending( + multi_signer: MultiSignerWrapper, + ) -> Result { debug!("certificate_pending"); - // Certificate pending - let certificate_pending = fake_data::certificate_pending(); + let certificate_pending_fake = fake_data::certificate_pending(); + let multi_signer = multi_signer.read().await; + + let beacon = certificate_pending_fake.beacon; - Ok(warp::reply::json(&certificate_pending)) + let protocol_parameters = multi_signer.get_protocol_parameters(); + if protocol_parameters.is_none() { + return Ok(warp::reply::with_status( + warp::reply::json(&entities::Error::new( + "MITHRIL-E0004".to_string(), + "no protocol parameters available".to_string(), + )), + StatusCode::INTERNAL_SERVER_ERROR, + )); + } + let protocol_parameters = protocol_parameters.unwrap().into(); + + let previous_hash = certificate_pending_fake.previous_hash; + + let signers = multi_signer.get_signers(); + if let Err(err) = signers { + return Ok(warp::reply::with_status( + warp::reply::json(&entities::Error::new( + "MITHRIL-E0007".to_string(), + err.to_string(), + )), + StatusCode::INTERNAL_SERVER_ERROR, + )); + } + let signers = signers.unwrap(); + + let certificate_pending = + entities::CertificatePending::new(beacon, protocol_parameters, previous_hash, signers); + + Ok(warp::reply::with_status( + warp::reply::json(&certificate_pending), + StatusCode::OK, + )) } /// Certificate by certificate hash @@ -180,7 +218,7 @@ mod handlers { .get_stake_distribution() .iter() .map(|(party_id, stake)| { - let verification_key = match multi_signer.get_signer(*party_id) { + let verification_key = match multi_signer.get_signer(*party_id).unwrap() { Some(verification_key) => { multi_signer::key_encode_hex(verification_key).unwrap() } @@ -215,7 +253,10 @@ mod handlers { StatusCode::NOT_FOUND, )), Err(err) => Ok(warp::reply::with_status( - warp::reply::json(&entities::Error::new("MITHRIL-E0005".to_string(), err)), + warp::reply::json(&entities::Error::new( + "MITHRIL-E0005".to_string(), + err.to_string(), + )), StatusCode::INTERNAL_SERVER_ERROR, )), } @@ -269,10 +310,39 @@ mod handlers { } /// Register Signer - pub async fn register_signer(signer: entities::Signer) -> Result { + pub async fn register_signer( + signer: entities::Signer, + multi_signer: MultiSignerWrapper, + ) -> Result { debug!("register_signer/{:?}", signer); - Ok(StatusCode::CREATED) + let mut multi_signer = multi_signer.write().await; + match key_decode_hex(&signer.verification_key) { + Ok(verification_key) => { + match multi_signer + .register_signer(signer.party_id as ProtocolPartyId, &verification_key) + { + Ok(()) => Ok(warp::reply::with_status( + warp::reply::json(&Null), + StatusCode::CREATED, + )), + Err(multi_signer::ProtocolError::ExistingSigner()) => Ok( + warp::reply::with_status(warp::reply::json(&Null), StatusCode::CONFLICT), + ), + Err(err) => Ok(warp::reply::with_status( + warp::reply::json(&entities::Error::new( + "MITHRIL-E0006".to_string(), + err.to_string(), + )), + StatusCode::INTERNAL_SERVER_ERROR, + )), + } + } + Err(_) => Ok(warp::reply::with_status( + warp::reply::json(&Null), + StatusCode::BAD_REQUEST, + )), + } } /// Register Signatures @@ -288,25 +358,33 @@ mod handlers { for signature in &signatures { match key_decode_hex(&signature.signature) { Ok(single_signature) => { - if let Err(err) = multi_signer.register_single_signature( + match multi_signer.register_single_signature( signature.party_id as multi_signer::ProtocolPartyId, &single_signature, signature.index as multi_signer::ProtocolLotteryIndex, ) { - return Ok(warp::reply::with_status( - warp::reply::json(&entities::Error::new( - "MITHRIL-E0003".to_string(), - err, - )), - StatusCode::INTERNAL_SERVER_ERROR, - )); + Err(multi_signer::ProtocolError::ExistingSingleSignature(_)) => { + return Ok(warp::reply::with_status( + warp::reply::json(&Null), + StatusCode::CONFLICT, + )); + } + Err(err) => { + return Ok(warp::reply::with_status( + warp::reply::json(&entities::Error::new( + "MITHRIL-E0003".to_string(), + err.to_string(), + )), + StatusCode::INTERNAL_SERVER_ERROR, + )); + } + _ => {} } } - Err(err) => { - // TODO: Depending on the error we should return a 409 or a 500 + Err(_) => { return Ok(warp::reply::with_status( - warp::reply::json(&entities::Error::new("MITHRIL-E0004".to_string(), err)), - StatusCode::INTERNAL_SERVER_ERROR, + warp::reply::json(&Null), + StatusCode::BAD_REQUEST, )); } } @@ -329,6 +407,8 @@ mod tests { use tokio::sync::RwLock; use warp::test::request; + use crate::multi_signer::ProtocolError; + use super::super::entities::*; use super::super::multi_signer::MockMultiSigner; use super::super::snapshot_store::MockSnapshotStorer; @@ -345,7 +425,76 @@ mod tests { #[tokio::test] async fn test_certificate_pending_get_ok() { - let dependency_manager = setup_dependency_manager(); + let fake_protocol_parameters = fake_data::protocol_parameters(); + let fake_signers_with_stakes = fake_data::signers_with_stakes(5); + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_get_protocol_parameters() + .return_once(|| Some(fake_protocol_parameters.into())); + mock_multi_signer + .expect_get_signers() + .return_once(|| Ok(fake_signers_with_stakes)); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let method = Method::GET.as_str(); + let path = "/certificate-pending"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&Null) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_certificate_pending_get_ko_protocol_parameters_500() { + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_get_protocol_parameters() + .return_once(|| None); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let method = Method::GET.as_str(); + let path = "/certificate-pending"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&Null) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_certificate_pending_get_ko_signers_500() { + let fake_protocol_parameters = fake_data::protocol_parameters(); + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_get_protocol_parameters() + .return_once(|| Some(fake_protocol_parameters.into())); + mock_multi_signer + .expect_get_signers() + .return_once(|| Err(ProtocolError::Codec("an error occurred".to_string()))); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + let method = Method::GET.as_str(); let path = "/certificate-pending"; @@ -427,7 +576,7 @@ mod tests { let mut mock_multi_signer = MockMultiSigner::new(); mock_multi_signer .expect_get_multi_signature() - .return_once(|_| Err("an error occurred".to_string())); + .return_once(|_| Err(ProtocolError::Codec("an error occurred".to_string()))); let mut dependency_manager = setup_dependency_manager(); dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); @@ -593,7 +742,101 @@ mod tests { #[tokio::test] async fn test_register_signer_post_ok() { - let dependency_manager = setup_dependency_manager(); + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_register_signer() + .return_once(|_, _| Ok(())); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let signer = &fake_data::signers(1)[0]; + + let method = Method::POST.as_str(); + let path = "/register-signer"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .json(signer) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&signer) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_register_signer_post_ko_400() { + let mock_multi_signer = MockMultiSigner::new(); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let mut signer = fake_data::signers(1)[0].clone(); + signer.verification_key = "invalid-key".to_string(); + + let method = Method::POST.as_str(); + let path = "/register-signer"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .json(&signer) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&signer) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_register_signer_post_ko_409() { + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_register_signer() + .return_once(|_, _| Err(ProtocolError::ExistingSigner())); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let signer = &fake_data::signers(1)[0]; + + let method = Method::POST.as_str(); + let path = "/register-signer"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .json(signer) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&signer) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_register_signer_post_ko_500() { + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_register_signer() + .return_once(|_, _| Err(ProtocolError::Core("an error occurred".to_string()))); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + let signer = &fake_data::signers(1)[0]; let method = Method::POST.as_str(); @@ -620,12 +863,10 @@ mod tests { let mut mock_multi_signer = MockMultiSigner::new(); mock_multi_signer .expect_update_current_message() - .return_const(Ok(())) - .once(); + .return_once(|_| Ok(())); mock_multi_signer .expect_register_single_signature() - .return_const(Ok(())) - .once(); + .return_once(|_, _, _| Ok(())); let mut dependency_manager = setup_dependency_manager(); dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); @@ -651,16 +892,78 @@ mod tests { } #[tokio::test] - async fn test_register_signatures_post_ko() { + async fn test_register_signatures_post_ko_400() { let mut mock_multi_signer = MockMultiSigner::new(); mock_multi_signer .expect_update_current_message() - .return_const(Ok(())) - .once(); + .return_once(|_| Ok(())); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let mut signatures = fake_data::single_signatures(1); + signatures[0].signature = "invalid-signature".to_string(); + + let method = Method::POST.as_str(); + let path = "/register-signatures"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .json(&signatures) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&signatures) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_register_signatures_post_ko_409() { + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_update_current_message() + .return_once(|_| Ok(())); mock_multi_signer .expect_register_single_signature() - .return_const(Err("an error occurred".to_string())) - .once(); + .return_once(|_, _, _| Err(ProtocolError::ExistingSingleSignature(1))); + let mut dependency_manager = setup_dependency_manager(); + dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); + + let signatures = &fake_data::single_signatures(1); + + let method = Method::POST.as_str(); + let path = "/register-signatures"; + + let response = request() + .method(method) + .path(&format!("/{}{}", SERVER_BASE_PATH, path)) + .json(signatures) + .reply(&router::routes(Arc::new(dependency_manager))) + .await; + + APISpec::from_file(API_SPEC_FILE) + .method(method) + .path(path) + .validate_request(&signatures) + .unwrap() + .validate_response(&response) + .expect("OpenAPI error"); + } + + #[tokio::test] + async fn test_register_signatures_post_ko_500() { + let mut mock_multi_signer = MockMultiSigner::new(); + mock_multi_signer + .expect_update_current_message() + .return_once(|_| Ok(())); + mock_multi_signer + .expect_register_single_signature() + .return_once(|_, _, _| Err(ProtocolError::Core("an error occurred".to_string()))); let mut dependency_manager = setup_dependency_manager(); dependency_manager.with_multi_signer(Arc::new(RwLock::new(mock_multi_signer))); diff --git a/mithril-aggregator/src/main.rs b/mithril-aggregator/src/main.rs index fe7f3cc1652..28fb75af5e1 100644 --- a/mithril-aggregator/src/main.rs +++ b/mithril-aggregator/src/main.rs @@ -3,8 +3,8 @@ use clap::Parser; use mithril_aggregator::{ - key_decode_hex, Config, DependencyManager, MultiSigner, MultiSignerImpl, ProtocolParameters, - ProtocolPartyId, ProtocolSignerVerificationKey, ProtocolStake, Server, SnapshotStoreHTTPClient, + Config, DependencyManager, MultiSigner, MultiSignerImpl, ProtocolPartyId, ProtocolStake, + Server, SnapshotStoreHTTPClient, }; use mithril_common::fake_data; use mithril_common::snapshotter::Snapshotter; @@ -13,6 +13,7 @@ use slog_scope::debug; use std::env; use std::sync::Arc; use tokio::sync::RwLock; + /// Node args #[derive(Parser, Debug, Clone)] pub struct Args { @@ -65,7 +66,7 @@ fn build_logger(min_level: Level) -> Logger { } #[tokio::main] -async fn main() { +async fn main() -> Result<(), String> { // Load args let args = Args::parse(); let _guard = slog_scope::set_global_logger(build_logger(args.log_level())); @@ -76,9 +77,9 @@ async fn main() { .add_source(config::File::with_name(&format!("./config/{}.json", run_mode)).required(false)) .add_source(config::Environment::default()) .build() - .unwrap() + .map_err(|e| format!("configuration build error: {}", e))? .try_deserialize() - .unwrap(); + .map_err(|e| format!("configuration deserialize error: {}", e))?; debug!("Started"; "run_mode" => &run_mode, "config" => format!("{:?}", config)); // Init dependencies @@ -119,6 +120,7 @@ async fn main() { handle.abort(); println!("Exiting..."); + Ok(()) } /// Init multi signer dependency @@ -127,13 +129,8 @@ fn init_multi_signer() -> impl MultiSigner { // Update protocol parameters let protocol_parameters = fake_data::protocol_parameters(); - let protocol_parameters = ProtocolParameters { - m: protocol_parameters.m, - k: protocol_parameters.k, - phi_f: protocol_parameters.phi_f as f64, - }; multi_signer - .update_protocol_parameters(&protocol_parameters) + .update_protocol_parameters(&protocol_parameters.into()) .expect("update protocol parameters failed"); // Update stake distribution @@ -151,15 +148,5 @@ fn init_multi_signer() -> impl MultiSigner { .update_stake_distribution(&stakes) .expect("stake distribution update failed"); - // Register signers - fake_data::signers(total_signers).iter().for_each(|signer| { - multi_signer - .register_signer( - signer.party_id as ProtocolPartyId, - &key_decode_hex::(&signer.verification_key).unwrap(), - ) - .expect("register signer failed"); - }); - multi_signer } diff --git a/mithril-aggregator/src/multi_signer.rs b/mithril-aggregator/src/multi_signer.rs index 54df5c31bf0..bd1b9a6cfaa 100644 --- a/mithril-aggregator/src/multi_signer.rs +++ b/mithril-aggregator/src/multi_signer.rs @@ -1,6 +1,7 @@ use hex::ToHex; use slog_scope::{debug, warn}; use std::collections::HashMap; +use thiserror::Error; // TODO: remove pub pub use mithril_common::crypto_helper::{ @@ -9,10 +10,29 @@ pub use mithril_common::crypto_helper::{ ProtocolSignerVerificationKey, ProtocolSingleSignature, ProtocolStake, ProtocolStakeDistribution, }; +use mithril_common::entities; #[cfg(test)] use mockall::automock; +#[derive(Error, Debug)] +pub enum ProtocolError { + #[error("signer already registered")] + ExistingSigner(), + + #[error("single signature already recorded")] + ExistingSingleSignature(ProtocolLotteryIndex), + + #[error("codec error: '{0}'")] + Codec(String), + + #[error("core error: '{0}'")] + Core(String), + + #[error("no message available")] + UnavailableMessage(), +} + /// MultiSigner is the cryptographic engine in charge of producing multi signatures from individual signatures #[cfg_attr(test, automock)] pub trait MultiSigner: Sync + Send { @@ -20,7 +40,7 @@ pub trait MultiSigner: Sync + Send { fn get_current_message(&self) -> Option; /// Update current message - fn update_current_message(&mut self, message: Bytes) -> Result<(), String>; + fn update_current_message(&mut self, message: Bytes) -> Result<(), ProtocolError>; /// Get protocol parameters fn get_protocol_parameters(&self) -> Option; @@ -29,7 +49,7 @@ pub trait MultiSigner: Sync + Send { fn update_protocol_parameters( &mut self, protocol_parameters: &ProtocolParameters, - ) -> Result<(), String>; + ) -> Result<(), ProtocolError>; /// Get stake distribution fn get_stake_distribution(&self) -> ProtocolStakeDistribution; @@ -38,17 +58,23 @@ pub trait MultiSigner: Sync + Send { fn update_stake_distribution( &mut self, stakes: &ProtocolStakeDistribution, - ) -> Result<(), String>; + ) -> Result<(), ProtocolError>; /// Register a signer fn register_signer( &mut self, party_id: ProtocolPartyId, verification_key: &ProtocolSignerVerificationKey, - ) -> Result<(), String>; + ) -> Result<(), ProtocolError>; /// Get signer - fn get_signer(&self, party_id: ProtocolPartyId) -> Option; + fn get_signer( + &self, + party_id: ProtocolPartyId, + ) -> Result, ProtocolError>; + + /// Get signers + fn get_signers(&self) -> Result, ProtocolError>; /// Registers a single signature fn register_single_signature( @@ -56,16 +82,16 @@ pub trait MultiSigner: Sync + Send { party_id: ProtocolPartyId, signature: &ProtocolSingleSignature, index: ProtocolLotteryIndex, - ) -> Result<(), String>; + ) -> Result<(), ProtocolError>; /// Retrieves a multi signature from a message fn get_multi_signature( &self, message: String, - ) -> Result, String>; + ) -> Result, ProtocolError>; /// Create a multi signature from single signatures - fn create_multi_signature(&mut self) -> Result, String>; + fn create_multi_signature(&mut self) -> Result, ProtocolError>; } /// MultiSignerImpl is an implementation of the MultiSigner @@ -105,7 +131,7 @@ impl MultiSignerImpl { let stakes = self.get_stake_distribution(); let mut key_registration = ProtocolKeyRegistration::init(&stakes); stakes.iter().for_each(|(party_id, _stake)| { - if let Some(verification_key) = self.get_signer(*party_id) { + if let Some(verification_key) = self.get_signer(*party_id).unwrap() { key_registration .register(*party_id, verification_key) .unwrap(); @@ -123,7 +149,7 @@ impl MultiSigner for MultiSignerImpl { } /// Update current message - fn update_current_message(&mut self, message: Bytes) -> Result<(), String> { + fn update_current_message(&mut self, message: Bytes) -> Result<(), ProtocolError> { self.current_message = Some(message); Ok(()) } @@ -137,7 +163,7 @@ impl MultiSigner for MultiSignerImpl { fn update_protocol_parameters( &mut self, protocol_parameters: &ProtocolParameters, - ) -> Result<(), String> { + ) -> Result<(), ProtocolError> { debug!("Update protocol parameters to {:?}", protocol_parameters); self.protocol_parameters = Some(protocol_parameters.to_owned()); Ok(()) @@ -152,15 +178,37 @@ impl MultiSigner for MultiSignerImpl { fn update_stake_distribution( &mut self, stakes: &ProtocolStakeDistribution, - ) -> Result<(), String> { + ) -> Result<(), ProtocolError> { debug!("Update stake distribution to {:?}", stakes); self.stakes = stakes.to_owned(); Ok(()) } /// Get signer verification key - fn get_signer(&self, party_id: ProtocolPartyId) -> Option { - self.signers.get(&party_id).copied() + fn get_signer( + &self, + party_id: ProtocolPartyId, + ) -> Result, ProtocolError> { + Ok(self.signers.get(&party_id).copied()) + } + + /// Get signers + fn get_signers(&self) -> Result, ProtocolError> { + Ok(self + .get_stake_distribution() + .iter() + .filter_map(|(party_id, stake)| match self.get_signer(*party_id) { + Ok(Some(verification_key)) => match key_encode_hex(verification_key) { + Ok(verification_key) => Some(entities::SignerWithStake::new( + *party_id as u64, + verification_key, + *stake as u64, + )), + Err(_) => None, + }, + _ => None, + }) + .collect()) } /// Register a signer @@ -168,10 +216,12 @@ impl MultiSigner for MultiSignerImpl { &mut self, party_id: ProtocolPartyId, verification_key: &ProtocolSignerVerificationKey, - ) -> Result<(), String> { + ) -> Result<(), ProtocolError> { debug!("Register signer {}", party_id); - self.signers.insert(party_id, *verification_key); - Ok(()) + match self.signers.insert(party_id, *verification_key) { + Some(_) => Err(ProtocolError::ExistingSigner()), + None => Ok(()), + } } /// Registers a single signature @@ -181,13 +231,15 @@ impl MultiSigner for MultiSignerImpl { party_id: ProtocolPartyId, signature: &ProtocolSingleSignature, index: ProtocolLotteryIndex, - ) -> Result<(), String> { + ) -> Result<(), ProtocolError> { debug!( "Register single signature from {} at index {}", party_id, index ); - let message = &self.get_current_message().unwrap(); + let message = &self + .get_current_message() + .ok_or_else(ProtocolError::UnavailableMessage)?; let clerk = self.clerk(); match clerk.verify_sig(signature, message) { Ok(_) => { @@ -202,7 +254,7 @@ impl MultiSigner for MultiSignerImpl { "Signature already registered from {} at index {}", party_id, index ); - return Err("signature already registered".to_string()); + return Err(ProtocolError::ExistingSingleSignature(index)); } None => { signatures.insert(index, signature.to_owned()); @@ -228,7 +280,7 @@ impl MultiSigner for MultiSignerImpl { Ok(()) } - Err(e) => Err(e.to_string()), + Err(e) => Err(ProtocolError::Core(e.to_string())), } } @@ -236,12 +288,12 @@ impl MultiSigner for MultiSignerImpl { fn get_multi_signature( &self, message: String, - ) -> Result, String> { + ) -> Result, ProtocolError> { debug!("Get multi signature for message {}", message); match self.multi_signatures.get(&message) { Some(multi_signature) => { - let multi_signature: ProtocolMultiSignature = key_decode_hex(multi_signature) - .map_err(|e| format!("can't decode multi signature: {}", e))?; + let multi_signature: ProtocolMultiSignature = + key_decode_hex(multi_signature).map_err(ProtocolError::Codec)?; Ok(Some(multi_signature)) } None => Ok(None), @@ -249,10 +301,12 @@ impl MultiSigner for MultiSignerImpl { } /// Create a multi signature from single signatures - fn create_multi_signature(&mut self) -> Result, String> { + fn create_multi_signature(&mut self) -> Result, ProtocolError> { debug!("Create multi signature"); - let message = &self.get_current_message().unwrap(); + let message = &self + .get_current_message() + .ok_or_else(ProtocolError::UnavailableMessage)?; let signatures: Vec = self .single_signatures .iter() @@ -272,12 +326,12 @@ impl MultiSigner for MultiSignerImpl { Ok(multi_signature) => { self.multi_signatures.insert( message.encode_hex::(), - key_encode_hex(&multi_signature).unwrap(), + key_encode_hex(&multi_signature).map_err(ProtocolError::Codec)?, ); self.single_signatures.drain(); Ok(Some(multi_signature)) } - Err(e) => Err(e.to_string()), + Err(err) => Err(ProtocolError::Core(err.to_string())), } } } @@ -324,7 +378,7 @@ mod tests { let stake_distribution_expected = setup_signers(5) .iter() - .map(|(party_id, stake, _, _)| (*party_id, *stake)) + .map(|(party_id, stake, _, _, _)| (*party_id, *stake)) .collect::<_>(); multi_signer .update_stake_distribution(&stake_distribution_expected) @@ -338,22 +392,44 @@ mod tests { fn test_multi_signer_register_signer_ok() { let mut multi_signer = MultiSignerImpl::new(); + let stake_distribution_expected = setup_signers(5) + .iter() + .map(|(party_id, stake, _, _, _)| (*party_id, *stake)) + .collect::<_>(); + multi_signer + .update_stake_distribution(&stake_distribution_expected) + .expect("update stake distribution failed"); + let signers = setup_signers(5); signers .iter() - .for_each(|(party_id, _, verification_key, _)| { + .for_each(|(party_id, _, verification_key, _, _)| { multi_signer .register_signer(*party_id, &verification_key) .expect("register should have succeeded") }); + let mut signers_all_expected = Vec::new(); signers .iter() - .for_each(|(party_id, _, verification_key_expected, _)| { + .for_each(|(party_id, stake, verification_key_expected, _, _)| { let verification_key = multi_signer.get_signer(*party_id); - assert!(verification_key.is_some()); - assert_eq!(*verification_key_expected, verification_key.unwrap()); + assert!(verification_key.as_ref().unwrap().is_some()); + assert_eq!( + *verification_key_expected, + verification_key.unwrap().unwrap() + ); + signers_all_expected.push(entities::SignerWithStake::new( + *party_id, + key_encode_hex(verification_key_expected).unwrap(), + *stake, + )); }); + + let signers_all = multi_signer + .get_signers() + .expect("get signers should have been succeeded"); + assert_eq!(signers_all_expected, signers_all); } #[test] @@ -373,14 +449,14 @@ mod tests { let signers = setup_signers(5); let stake_distribution = &signers .iter() - .map(|(party_id, stake, _, _)| (*party_id, *stake)) + .map(|(party_id, stake, _, _, _)| (*party_id, *stake)) .collect::<_>(); multi_signer .update_stake_distribution(&stake_distribution) .expect("update stake distribution failed"); signers .iter() - .for_each(|(party_id, _, verification_key, _)| { + .for_each(|(party_id, _, verification_key, _, _)| { multi_signer .register_signer(*party_id, verification_key) .expect("register should have succeeded") @@ -389,7 +465,7 @@ mod tests { let mut signatures = Vec::new(); signers .iter() - .for_each(|(party_id, _, _, protocol_signer)| { + .for_each(|(party_id, _, _, protocol_signer, _)| { for i in 1..=protocol_parameters.m { if let Some(signature) = protocol_signer.sign(&message, i) { signatures.push((*party_id, signature, i as ProtocolLotteryIndex)); diff --git a/mithril-client/src/verifier.rs b/mithril-client/src/verifier.rs index d3e9c9f3154..e9735f9e084 100644 --- a/mithril-client/src/verifier.rs +++ b/mithril-client/src/verifier.rs @@ -3,7 +3,7 @@ use thiserror::Error; use mithril_common::crypto_helper::{ key_decode_hex, Bytes, ProtocolClerk, ProtocolKeyRegistration, ProtocolMultiSignature, - ProtocolParameters, ProtocolPartyId, ProtocolStake, ProtocolStakeDistribution, + ProtocolPartyId, ProtocolStake, ProtocolStakeDistribution, }; use crate::entities; @@ -46,11 +46,6 @@ impl VerifierImpl { signers_with_stakes: &[entities::SignerWithStake], protocol_parameters: &entities::ProtocolParameters, ) -> Result { - let protocol_parameters = ProtocolParameters { - k: protocol_parameters.k, - m: protocol_parameters.m, - phi_f: protocol_parameters.phi_f as f64, - }; let stakes = signers_with_stakes .iter() .map(|signer| { @@ -70,7 +65,7 @@ impl VerifierImpl { }); let closed_registration = key_registration.close(); Ok(ProtocolClerk::from_registration( - protocol_parameters, + protocol_parameters.to_owned().into(), closed_registration, )) } @@ -117,7 +112,7 @@ mod tests { let message = setup_message(); let mut single_signatures = Vec::new(); - signers.iter().for_each(|(_, _, _, protocol_signer)| { + signers.iter().for_each(|(_, _, _, protocol_signer, _)| { for i in 1..=protocol_parameters.m { if let Some(signature) = protocol_signer.sign(&message, i) { single_signatures.push(signature); @@ -130,14 +125,10 @@ mod tests { let multi_signature = clerk.aggregate(&single_signatures, &message).unwrap(); let verifier = VerifierImpl::new(); - let protocol_parameters = entities::ProtocolParameters { - k: protocol_parameters.k, - m: protocol_parameters.m, - phi_f: protocol_parameters.phi_f as f32, - }; + let protocol_parameters = protocol_parameters.into(); let signers_with_stakes = signers .iter() - .map(|(party_id, stake, verification_key, _)| { + .map(|(party_id, stake, verification_key, _, _)| { entities::SignerWithStake::new( *party_id as u64, key_encode_hex(verification_key).unwrap(), diff --git a/mithril-common/src/crypto_helper/codec.rs b/mithril-common/src/crypto_helper/codec.rs index c0ff3be4f3c..fe847e4cff4 100644 --- a/mithril-common/src/crypto_helper/codec.rs +++ b/mithril-common/src/crypto_helper/codec.rs @@ -30,10 +30,6 @@ pub mod tests { use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; - pub fn message() -> Bytes { - Vec::from_hex("7724e03fb8d84a376a43b8f41518a11c").unwrap() - } - #[test] fn test_key_encode_decode_hex() { let protocol_params = setup_protocol_parameters(); diff --git a/mithril-common/src/crypto_helper/tests_setup.rs b/mithril-common/src/crypto_helper/tests_setup.rs index 18b8e410c84..908bf750080 100644 --- a/mithril-common/src/crypto_helper/tests_setup.rs +++ b/mithril-common/src/crypto_helper/tests_setup.rs @@ -23,6 +23,7 @@ pub fn setup_signers( ProtocolStake, ProtocolSignerVerificationKey, ProtocolSigner, + ProtocolInitializer, )> { let seed = [0u8; 32]; let mut rng = ChaCha20Rng::from_seed(seed); @@ -66,7 +67,10 @@ pub fn setup_signers( party_id, stake, protocol_initializer.verification_key(), - protocol_initializer.new_signer(closed_key_registration.clone()), + protocol_initializer + .clone() + .new_signer(closed_key_registration.clone()), + protocol_initializer, ) }) .collect::<_>() diff --git a/mithril-common/src/crypto_helper/types.rs b/mithril-common/src/crypto_helper/types.rs index dfad6e3a355..a616b7593b0 100644 --- a/mithril-common/src/crypto_helper/types.rs +++ b/mithril-common/src/crypto_helper/types.rs @@ -1,10 +1,11 @@ use mithril::key_reg::KeyReg; -use mithril::multi_sig::SigningKey; use mithril::stm::{ Index, PartyId, Stake, StmClerk, StmInitializer, StmMultiSig, StmParameters, StmSig, StmSigner, StmVerificationKey, }; +use super::super::entities; + pub type Bytes = Vec; // Protocol types alias @@ -21,4 +22,49 @@ pub type ProtocolKeyRegistration = KeyReg; pub type ProtocolSingleSignature = StmSig; pub type ProtocolMultiSignature = StmMultiSig; pub type ProtocolSignerVerificationKey = StmVerificationKey; -pub type ProtocolSignerSecretKey = SigningKey; + +impl From for entities::ProtocolParameters { + fn from(other: ProtocolParameters) -> Self { + entities::ProtocolParameters::new(other.k, other.m, other.phi_f as f32) + } +} + +impl From for ProtocolParameters { + fn from(other: entities::ProtocolParameters) -> Self { + ProtocolParameters { + k: other.k, + m: other.m, + phi_f: other.phi_f as f64, + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn test_protocol_parameters_from_into() { + let protocol_parameters_expected = ProtocolParameters { + k: 100, + m: 1000, + phi_f: 1.0, + }; + let protocol_initializer_entities_expected = entities::ProtocolParameters::new( + protocol_parameters_expected.k, + protocol_parameters_expected.m, + protocol_parameters_expected.phi_f as f32, + ); + + let protocol_initializer_entities_into: entities::ProtocolParameters = + protocol_parameters_expected.into(); + assert_eq!( + protocol_initializer_entities_expected, + protocol_initializer_entities_into + ); + + let protocol_initializer_from: ProtocolParameters = + protocol_initializer_entities_expected.into(); + assert_eq!(protocol_parameters_expected, protocol_initializer_from); + } +} diff --git a/mithril-common/src/entities.rs b/mithril-common/src/entities.rs index 797349c3a82..88c57e62639 100644 --- a/mithril-common/src/entities.rs +++ b/mithril-common/src/entities.rs @@ -46,6 +46,7 @@ pub struct CertificatePending { pub previous_hash: String, /// Current Signers with stakes + // TODO: Should return Vec instead, will be updated when stake distribution is real #[serde(rename = "signers")] pub signers: Vec, } diff --git a/mithril-common/src/fake_data.rs b/mithril-common/src/fake_data.rs index 33e1399645b..92a491ce28e 100644 --- a/mithril-common/src/fake_data.rs +++ b/mithril-common/src/fake_data.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use serde::{Deserialize, Serialize}; use std::time::{SystemTime, UNIX_EPOCH}; use crate::entities; @@ -102,28 +101,6 @@ pub fn signers(total: u64) -> Vec { .collect::>() } -/// SignerWithSecretKeys represents a signer with verification/secret keys -// TODO: To delete once key registration is implemented -#[derive(Serialize, Deserialize, Clone)] -pub struct SignerWithSecretKeys { - pub party_id: u64, - pub stake: u64, - pub verification_key: String, - pub secret_key: String, -} - -/// Fake SignerKeys returns Verification/Secret keys for a party_id -// TODO: To delete once key registration is implemented -pub fn signer_keys(party_id: u64) -> Option { - let signers_with_secret_keys = - serde_json::from_str::>(SIGNERS_WITH_STAKE_JSON).unwrap(); - let signer_keys = signers_with_secret_keys - .iter() - .filter(|signer| signer.party_id == party_id) - .collect::>(); - Some((*(signer_keys.first()?)).clone()) -} - /// Fake SingleSignatures pub fn single_signatures(total: u64) -> Vec { (1..total + 1) diff --git a/mithril-signer/src/certificate_handler.rs b/mithril-signer/src/certificate_handler.rs index 44a4c524b47..ffaf22d55cd 100644 --- a/mithril-signer/src/certificate_handler.rs +++ b/mithril-signer/src/certificate_handler.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; -use mithril_common::entities::CertificatePending; -use mithril_common::entities::SingleSignature; use reqwest::{self, StatusCode}; use slog_scope::debug; use std::io; use thiserror::Error; +use mithril_common::entities::{CertificatePending, Signer, SingleSignature}; + #[cfg(test)] use mockall::automock; @@ -31,6 +31,9 @@ pub trait CertificateHandler { &self, ) -> Result, CertificateHandlerError>; + /// Registers signer with the aggregator + async fn register_signer(&self, signer: &Signer) -> Result<(), CertificateHandlerError>; + /// Registers single signatures with the aggregator async fn register_signatures( &self, @@ -78,6 +81,30 @@ impl CertificateHandler for CertificateHandlerHTTPClient { } } + async fn register_signer(&self, signer: &Signer) -> Result<(), CertificateHandlerError> { + debug!("Register signer"); + let url = format!("{}/register-signer", self.aggregator_endpoint); + let client = reqwest::Client::new(); + let response = client.post(url.clone()).json(signer).send().await; + match response { + Ok(response) => match response.status() { + StatusCode::CREATED => Ok(()), + StatusCode::BAD_REQUEST => Err(CertificateHandlerError::RemoteServerLogical( + "bad request".to_string(), + )), + StatusCode::CONFLICT => Err(CertificateHandlerError::RemoteServerLogical( + "already registered signer".to_string(), + )), + status_error => Err(CertificateHandlerError::RemoteServerTechnical( + status_error.to_string(), + )), + }, + Err(err) => Err(CertificateHandlerError::RemoteServerUnreachable( + err.to_string(), + )), + } + } + async fn register_signatures( &self, signatures: &[SingleSignature], @@ -173,6 +200,73 @@ mod tests { ); } + #[tokio::test] + async fn test_register_signer_ok_201() { + let single_signers = fake_data::signers(1); + let single_signer = single_signers.first().unwrap(); + let (server, config) = setup_test(); + let _snapshots_mock = server.mock(|when, then| { + when.method(POST).path("/register-signer"); + then.status(201); + }); + let certificate_handler = CertificateHandlerHTTPClient::new(config.aggregator_endpoint); + let register_signer = certificate_handler.register_signer(&single_signer).await; + register_signer.expect("unexpected error"); + } + + #[tokio::test] + async fn test_register_signer_ok_400() { + let single_signers = fake_data::signers(1); + let single_signer = single_signers.first().unwrap(); + let (server, config) = setup_test(); + let _snapshots_mock = server.mock(|when, then| { + when.method(POST).path("/register-signer"); + then.status(400); + }); + let certificate_handler = CertificateHandlerHTTPClient::new(config.aggregator_endpoint); + let register_signer = certificate_handler.register_signer(&single_signer).await; + assert_eq!( + CertificateHandlerError::RemoteServerLogical("bad request".to_string()).to_string(), + register_signer.unwrap_err().to_string() + ); + } + + #[tokio::test] + async fn test_register_signer_ok_409() { + let single_signers = fake_data::signers(1); + let single_signer = single_signers.first().unwrap(); + let (server, config) = setup_test(); + let _snapshots_mock = server.mock(|when, then| { + when.method(POST).path("/register-signer"); + then.status(409); + }); + let certificate_handler = CertificateHandlerHTTPClient::new(config.aggregator_endpoint); + let register_signer = certificate_handler.register_signer(&single_signer).await; + assert_eq!( + CertificateHandlerError::RemoteServerLogical("already registered signer".to_string()) + .to_string(), + register_signer.unwrap_err().to_string() + ); + } + + #[tokio::test] + async fn test_register_signer_ok_500() { + let single_signers = fake_data::signers(1); + let single_signer = single_signers.first().unwrap(); + let (server, config) = setup_test(); + let _snapshots_mock = server.mock(|when, then| { + when.method(POST).path("/register-signer"); + then.status(500); + }); + let certificate_handler = CertificateHandlerHTTPClient::new(config.aggregator_endpoint); + let register_signer = certificate_handler.register_signer(&single_signer).await; + assert_eq!( + CertificateHandlerError::RemoteServerTechnical("500 Internal Server Error".to_string()) + .to_string(), + register_signer.unwrap_err().to_string() + ); + } + #[tokio::test] async fn test_register_signatures_ok_201() { let single_signatures = fake_data::single_signatures(5); diff --git a/mithril-signer/src/lib.rs b/mithril-signer/src/lib.rs index 3b9eb24c52d..3355ecb8251 100644 --- a/mithril-signer/src/lib.rs +++ b/mithril-signer/src/lib.rs @@ -1,9 +1,9 @@ mod certificate_handler; mod entities; -mod signer; +mod runtime; mod single_signer; pub use certificate_handler::CertificateHandlerHTTPClient; pub use entities::Config; -pub use signer::Signer; +pub use runtime::Runtime; pub use single_signer::MithrilSingleSigner; diff --git a/mithril-signer/src/main.rs b/mithril-signer/src/main.rs index 86300ca0bc8..0c0c844b871 100644 --- a/mithril-signer/src/main.rs +++ b/mithril-signer/src/main.rs @@ -1,14 +1,10 @@ use clap::Parser; use slog::{o, Drain, Level, Logger}; -use slog_scope::{debug, error, info}; +use slog_scope::debug; use std::env; use std::sync::Arc; -use std::time::Duration; -use tokio::time::sleep; -use mithril_common::crypto_helper::key_decode_hex; -use mithril_common::fake_data; -use mithril_signer::{CertificateHandlerHTTPClient, Config, MithrilSingleSigner, Signer}; +use mithril_signer::{CertificateHandlerHTTPClient, Config, MithrilSingleSigner, Runtime}; /// CLI args #[derive(Parser)] @@ -47,7 +43,7 @@ fn build_logger(min_level: Level) -> Logger { } #[tokio::main] -async fn main() { +async fn main() -> Result<(), String> { // Load args let args = Args::parse(); let _guard = slog_scope::set_global_logger(build_logger(args.log_level())); @@ -58,25 +54,21 @@ async fn main() { .add_source(config::File::with_name(&format!("./config/{}.json", run_mode)).required(false)) .add_source(config::Environment::default()) .build() - .unwrap() + .map_err(|e| format!("configuration build error: {}", e))? .try_deserialize() - .unwrap(); + .map_err(|e| format!("configuration deserialize error: {}", e))?; debug!("Started"; "run_mode" => &run_mode, "config" => format!("{:?}", config)); - loop { - let fake_signer = fake_data::signer_keys(config.party_id).unwrap(); - let single_signer = MithrilSingleSigner::new( - fake_signer.party_id, - key_decode_hex(&fake_signer.secret_key).unwrap(), - ); - let certificate_handler = - CertificateHandlerHTTPClient::new(config.aggregator_endpoint.clone()); + // TODO: Use serialized ProtocolInitializer here, loaded e.g. from filesystem + let protocol_initializer_encoded = ""; + let single_signer = MithrilSingleSigner::new(config.party_id, protocol_initializer_encoded); + let certificate_handler = CertificateHandlerHTTPClient::new(config.aggregator_endpoint.clone()); + let mut runtime = Runtime::new(Box::new(certificate_handler), Box::new(single_signer)); - let mut signer = Signer::new(Box::new(certificate_handler), Box::new(single_signer)); - if let Err(e) = signer.run().await { - error!("{:?}", e) - } - info!("Sleeping for {}", config.run_interval); - sleep(Duration::from_millis(config.run_interval)).await; - } + runtime + .infinite_loop(&config) + .await + .map_err(|e| e.to_string())?; + + Ok(()) } diff --git a/mithril-signer/src/runtime.rs b/mithril-signer/src/runtime.rs new file mode 100644 index 00000000000..58a314b455d --- /dev/null +++ b/mithril-signer/src/runtime.rs @@ -0,0 +1,416 @@ +use std::collections::HashMap; +use std::time::Duration; +use thiserror::Error; + +use mithril_common::crypto_helper::key_encode_hex; +use mithril_common::entities::{self, Beacon, CertificatePending, SignerWithStake}; +use mithril_common::fake_data; + +use crate::Config; + +use super::certificate_handler::CertificateHandler; +use super::single_signer::SingleSigner; +use slog_scope::{error, info}; +use tokio::time::sleep; + +pub struct Runtime { + certificate_handler: Box, + single_signer: Box, + current_beacon: Option, +} + +#[derive(Error, Debug, PartialEq)] +pub enum SignerError { + #[error("single signatures computation failed: `{0}`")] + SingleSignaturesComputeFailed(String), + #[error("could not retrieve pending certificate: `{0}`")] + RetrievePendingCertificateFailed(String), + #[error("could not retrieve protocol initializer")] + RetrieveProtocolInitializerFailed(), + #[error("register signer failed: `{0}`")] + RegisterSignerFailed(String), + #[error("codec error:`{0}`")] + Codec(String), +} + +impl Runtime { + pub fn new( + certificate_handler: Box, + single_signer: Box, + ) -> Self { + Self { + certificate_handler, + single_signer, + current_beacon: None, + } + } + + async fn register_signer(&mut self) -> Result<(), SignerError> { + if let Some(protocol_initializer) = self.single_signer.get_protocol_initializer() { + let verification_key = protocol_initializer.verification_key(); + let verification_key = key_encode_hex(verification_key).map_err(SignerError::Codec)?; + let signer = entities::Signer::new(self.single_signer.get_party_id(), verification_key); + self.certificate_handler + .register_signer(&signer) + .await + .map_err(|e| SignerError::RegisterSignerFailed(e.to_string()))?; + self.single_signer + .update_is_registered(true) + .map_err(|e| SignerError::RegisterSignerFailed(e.to_string()))?; + } + + Ok(()) + } + + async fn register_signature( + &mut self, + pending_certificate: CertificatePending, + ) -> Result<(), SignerError> { + let message = fake_data::digest(); + let stake_distribution = fake_data::signers_with_stakes(5); + let verification_keys = pending_certificate + .signers + .iter() + .map(|signer| (signer.party_id, signer.verification_key.as_str())) + .collect::>(); + let stake_distribution_extended = stake_distribution + .into_iter() + .map(|signer| { + let verification_key = match verification_keys.get(&signer.party_id) { + Some(verification_key_found) => *verification_key_found, + None => "", + }; + SignerWithStake::new(signer.party_id, verification_key.to_string(), signer.stake) + }) + .collect::>(); + let signatures = self + .single_signer + .compute_single_signatures( + message, + stake_distribution_extended, + &pending_certificate.protocol_parameters, + ) + .map_err(|e| SignerError::SingleSignaturesComputeFailed(e.to_string()))?; + + if !signatures.is_empty() { + let _ = self + .certificate_handler + .register_signatures(&signatures) + .await; + } + self.current_beacon = Some(pending_certificate.beacon); + + Ok(()) + } + + pub async fn check_pending_certificate(&mut self) -> Result<(), SignerError> { + if let Some(pending_certificate) = self + .certificate_handler + .retrieve_pending_certificate() + .await + .map_err(|e| SignerError::RetrievePendingCertificateFailed(e.to_string()))? + { + if !self.single_signer.get_is_registered() { + self.register_signer().await?; + } + + if match &self.current_beacon { + None => true, + Some(beacon) => beacon != &pending_certificate.beacon, + } { + self.register_signature(pending_certificate).await?; + } + } + + Ok(()) + } + + pub async fn infinite_loop(&mut self, config: &Config) -> Result<(), SignerError> { + loop { + self.check_pending_certificate().await?; + info!("Sleeping for {}", config.run_interval); + sleep(Duration::from_millis(config.run_interval)).await; + } + + #[allow(unreachable_code)] + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::super::certificate_handler::{CertificateHandlerError, MockCertificateHandler}; + use super::super::single_signer::{MockSingleSigner, SingleSignerError}; + use super::*; + use mithril_common::crypto_helper::tests_setup::*; + use mithril_common::fake_data; + + #[tokio::test] + async fn signer_doesnt_sign_when_there_is_no_pending_certificate() { + let current_signer = &setup_signers(1)[0]; + let party_id = current_signer.clone().0; + let protocol_initializer = current_signer.4.clone(); + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| Ok(None)); + mock_certificate_handler + .expect_register_signer() + .return_once(|_| Ok(())); + mock_single_signer + .expect_compute_single_signatures() + .never(); + mock_single_signer + .expect_get_party_id() + .return_once(move || party_id); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || Some(protocol_initializer)); + mock_single_signer + .expect_get_is_registered() + .return_once(|| false); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert!(runtime.check_pending_certificate().await.is_ok()); + } + + #[tokio::test] + async fn signer_fails_when_pending_certificate_fails() { + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| { + Err(CertificateHandlerError::RemoteServerTechnical( + "An Error".to_string(), + )) + }); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || None); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert_eq!( + SignerError::RetrievePendingCertificateFailed( + CertificateHandlerError::RemoteServerTechnical("An Error".to_string()).to_string() + ), + runtime.check_pending_certificate().await.unwrap_err() + ); + } + + #[tokio::test] + async fn signer_sign_when_triggered_by_pending_certificate() { + let current_signer = &setup_signers(1)[0]; + let party_id = current_signer.clone().0; + let protocol_initializer = current_signer.4.clone(); + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + let pending_certificate = fake_data::certificate_pending(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .returning(|| Ok(None)) + .once(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| Ok(Some(pending_certificate))); + mock_certificate_handler + .expect_register_signer() + .returning(|_| Ok(())) + .times(1); + mock_certificate_handler + .expect_register_signatures() + .return_once(|_| Ok(())); + mock_single_signer + .expect_compute_single_signatures() + .return_once(|_, _, _| Ok(fake_data::single_signatures(2))); + mock_single_signer + .expect_get_party_id() + .return_once(move || party_id); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || Some(protocol_initializer.clone())); + mock_single_signer + .expect_get_is_registered() + .return_once(|| false); + mock_single_signer + .expect_update_is_registered() + .return_once(move |_| Ok(())); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert!(runtime.check_pending_certificate().await.is_ok()); + assert!(runtime.check_pending_certificate().await.is_ok()); + } + + #[tokio::test] + async fn signer_sign_only_once_if_pending_certificate_has_not_changed() { + let current_signer = &setup_signers(1)[0]; + let party_id = current_signer.clone().0; + let protocol_initializer = current_signer.4.clone(); + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + let pending_certificate = fake_data::certificate_pending(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .returning(move || Ok(Some(pending_certificate.clone()))) + .times(2); + mock_certificate_handler + .expect_register_signatures() + .return_once(|_| Ok(())); + mock_certificate_handler + .expect_register_signer() + .returning(|_| Ok(())) + .times(1); + mock_single_signer + .expect_compute_single_signatures() + .return_once(|_, _, _| Ok(fake_data::single_signatures(2))); + mock_single_signer + .expect_get_party_id() + .returning(move || party_id) + .once(); + mock_single_signer + .expect_get_is_registered() + .returning(|| false) + .once(); + mock_single_signer + .expect_get_is_registered() + .returning(|| true) + .once(); + mock_single_signer + .expect_update_is_registered() + .return_once(move |_| Ok(())); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || Some(protocol_initializer.clone())); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert!(runtime.check_pending_certificate().await.is_ok()); + assert!(runtime.check_pending_certificate().await.is_ok()); + } + + #[tokio::test] + async fn signer_does_not_send_signatures_if_none_are_computed() { + let current_signer = &setup_signers(1)[0]; + let party_id = current_signer.clone().0; + let protocol_initializer = current_signer.4.clone(); + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + let pending_certificate = fake_data::certificate_pending(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| Ok(Some(pending_certificate))); + mock_certificate_handler + .expect_register_signatures() + .never(); + mock_certificate_handler + .expect_register_signer() + .return_once(|_| Ok(())); + mock_single_signer + .expect_compute_single_signatures() + .return_once(|_, _, _| Ok(fake_data::single_signatures(0))); + mock_single_signer + .expect_get_party_id() + .return_once(move || party_id); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || Some(protocol_initializer)); + mock_single_signer + .expect_get_is_registered() + .return_once(|| false); + mock_single_signer + .expect_update_is_registered() + .return_once(move |_| Ok(())); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert!(runtime.check_pending_certificate().await.is_ok()); + } + + #[tokio::test] + async fn signer_fails_if_signature_computation_fails() { + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + let pending_certificate = fake_data::certificate_pending(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| Ok(Some(pending_certificate))); + mock_single_signer + .expect_compute_single_signatures() + .return_once(|_, _, _| Err(SingleSignerError::UnregisteredVerificationKey())); + mock_single_signer + .expect_get_is_registered() + .return_once(|| false); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || None); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert_eq!( + SignerError::SingleSignaturesComputeFailed( + SingleSignerError::UnregisteredVerificationKey().to_string() + ), + runtime.check_pending_certificate().await.unwrap_err() + ); + } + + #[tokio::test] + async fn signer_fails_when_register_signer_fails() { + let current_signer = &setup_signers(1)[0]; + let party_id = current_signer.clone().0; + let protocol_initializer = current_signer.4.clone(); + let pending_certificate = fake_data::certificate_pending(); + let mut mock_certificate_handler = MockCertificateHandler::new(); + let mut mock_single_signer = MockSingleSigner::new(); + mock_certificate_handler + .expect_retrieve_pending_certificate() + .return_once(|| Ok(Some(pending_certificate))); + mock_certificate_handler + .expect_register_signer() + .return_once(|_| { + Err(CertificateHandlerError::RemoteServerLogical( + "an error occurred".to_string(), + )) + }); + mock_single_signer + .expect_compute_single_signatures() + .never(); + mock_single_signer + .expect_get_party_id() + .return_once(move || party_id); + mock_single_signer + .expect_get_protocol_initializer() + .return_once(move || Some(protocol_initializer)); + mock_single_signer + .expect_get_is_registered() + .return_once(|| false); + + let mut runtime = Runtime::new( + Box::new(mock_certificate_handler), + Box::new(mock_single_signer), + ); + assert_eq!( + SignerError::RegisterSignerFailed( + CertificateHandlerError::RemoteServerLogical("an error occurred".to_string(),) + .to_string() + ), + runtime.check_pending_certificate().await.unwrap_err() + ); + } +} diff --git a/mithril-signer/src/signer.rs b/mithril-signer/src/signer.rs deleted file mode 100644 index 325fb2017f1..00000000000 --- a/mithril-signer/src/signer.rs +++ /dev/null @@ -1,218 +0,0 @@ -use thiserror::Error; - -use mithril_common::entities::Beacon; -use mithril_common::fake_data; - -use super::certificate_handler::CertificateHandler; -use super::single_signer::SingleSigner; - -pub struct Signer { - certificate_handler: Box, - single_signer: Box, - current_beacon: Option, -} - -#[derive(Error, Debug, PartialEq)] -pub enum SignerError { - #[error("single signatures computation failed: `{0}`")] - SingleSignaturesComputeFailed(String), - #[error("could not retrieve pending certificate: `{0}`")] - RetrievePendingCertificateFailed(String), -} - -impl Signer { - pub fn new( - certificate_handler: Box, - single_signer: Box, - ) -> Self { - Self { - certificate_handler, - single_signer, - current_beacon: None, - } - } - - pub async fn run(&mut self) -> Result<(), SignerError> { - if let Some(pending_certificate) = self - .certificate_handler - .retrieve_pending_certificate() - .await - .map_err(|e| SignerError::RetrievePendingCertificateFailed(e.to_string()))? - { - let must_register_signature = match &self.current_beacon { - None => { - self.current_beacon = Some(pending_certificate.beacon); - true - } - Some(beacon) => beacon != &pending_certificate.beacon, - }; - - if must_register_signature { - let message = fake_data::digest(); - let stake_distribution = fake_data::signers_with_stakes(5); - let signatures = self - .single_signer - .compute_single_signatures( - message, - stake_distribution, - &pending_certificate.protocol_parameters, - ) - .map_err(|e| SignerError::SingleSignaturesComputeFailed(e.to_string()))?; - if !signatures.is_empty() { - let _ = self - .certificate_handler - .register_signatures(&signatures) - .await; - } - } - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::super::certificate_handler::{CertificateHandlerError, MockCertificateHandler}; - use super::super::single_signer::{MockSingleSigner, SingleSignerError}; - use super::*; - use mithril_common::fake_data; - - #[tokio::test] - async fn signer_doesnt_sign_when_there_is_no_pending_certificate() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - let mut mock_single_signer = MockSingleSigner::new(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .return_once(|| Ok(None)); - mock_single_signer - .expect_compute_single_signatures() - .never(); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(mock_single_signer), - ); - assert!(signer.run().await.is_ok()); - } - - #[tokio::test] - async fn signer_fails_when_pending_certificate_fails() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .return_once(|| { - Err(CertificateHandlerError::RemoteServerTechnical( - "An Error".to_string(), - )) - }); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(MockSingleSigner::new()), - ); - assert_eq!( - SignerError::RetrievePendingCertificateFailed( - CertificateHandlerError::RemoteServerTechnical("An Error".to_string()).to_string() - ), - signer.run().await.unwrap_err() - ); - } - - #[tokio::test] - async fn signer_sign_when_triggered_by_pending_certificate() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - let mut mock_single_signer = MockSingleSigner::new(); - let pending_certificate = fake_data::certificate_pending(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .returning(|| Ok(None)) - .once(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .return_once(|| Ok(Some(pending_certificate))); - mock_certificate_handler - .expect_register_signatures() - .return_once(|_| Ok(())); - mock_single_signer - .expect_compute_single_signatures() - .return_once(|_, _, _| Ok(fake_data::single_signatures(2))); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(mock_single_signer), - ); - assert!(signer.run().await.is_ok()); - assert!(signer.run().await.is_ok()); - } - - #[tokio::test] - async fn signer_sign_only_once_if_pending_certificate_has_not_changed() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - let mut mock_single_signer = MockSingleSigner::new(); - let pending_certificate = fake_data::certificate_pending(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .returning(move || Ok(Some(pending_certificate.clone()))) - .times(2); - mock_certificate_handler - .expect_register_signatures() - .return_once(|_| Ok(())); - mock_single_signer - .expect_compute_single_signatures() - .return_once(|_, _, _| Ok(fake_data::single_signatures(2))); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(mock_single_signer), - ); - assert!(signer.run().await.is_ok()); - assert!(signer.run().await.is_ok()); - } - - #[tokio::test] - async fn signer_does_not_send_signatures_if_none_are_computed() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - let mut mock_single_signer = MockSingleSigner::new(); - let pending_certificate = fake_data::certificate_pending(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .return_once(|| Ok(Some(pending_certificate))); - mock_certificate_handler - .expect_register_signatures() - .never(); - mock_single_signer - .expect_compute_single_signatures() - .return_once(|_, _, _| Ok(fake_data::single_signatures(0))); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(mock_single_signer), - ); - assert!(signer.run().await.is_ok()); - } - - #[tokio::test] - async fn signer_fails_if_signature_computation_fails() { - let mut mock_certificate_handler = MockCertificateHandler::new(); - let mut mock_single_signer = MockSingleSigner::new(); - let pending_certificate = fake_data::certificate_pending(); - mock_certificate_handler - .expect_retrieve_pending_certificate() - .return_once(|| Ok(Some(pending_certificate))); - mock_single_signer - .expect_compute_single_signatures() - .return_once(|_, _, _| Err(SingleSignerError::UnregisteredVerificationKey())); - - let mut signer = Signer::new( - Box::new(mock_certificate_handler), - Box::new(mock_single_signer), - ); - assert_eq!( - SignerError::SingleSignaturesComputeFailed( - SingleSignerError::UnregisteredVerificationKey().to_string() - ), - signer.run().await.unwrap_err() - ); - } -} diff --git a/mithril-signer/src/single_signer.rs b/mithril-signer/src/single_signer.rs index 307bb433d3d..ba988039adb 100644 --- a/mithril-signer/src/single_signer.rs +++ b/mithril-signer/src/single_signer.rs @@ -4,7 +4,7 @@ use thiserror::Error; use mithril_common::crypto_helper::{ key_decode_hex, key_encode_hex, Bytes, ProtocolInitializer, ProtocolKeyRegistration, - ProtocolParameters, ProtocolPartyId, ProtocolSigner, ProtocolSignerSecretKey, ProtocolStake, + ProtocolPartyId, ProtocolSigner, ProtocolStake, }; use mithril_common::entities::{self, SignerWithStake, SingleSignature}; @@ -13,12 +13,25 @@ use mockall::automock; #[cfg_attr(test, automock)] pub trait SingleSigner { + /// Get party id + fn get_party_id(&self) -> ProtocolPartyId; + + /// Get currently setup protocol initializer + fn get_protocol_initializer(&self) -> Option; + + /// Get is registered signer + fn get_is_registered(&self) -> bool; + + /// Update is registered signer + fn update_is_registered(&mut self, is_registered: bool) -> Result<(), SingleSignerError>; + + /// Computes single signatures fn compute_single_signatures( - &self, + &mut self, message: Bytes, - stake_distribution: Vec, + stake_distribution: Vec, protocol_parameters: &entities::ProtocolParameters, - ) -> Result, SingleSignerError>; + ) -> Result, SingleSignerError>; } #[derive(Error, Debug, PartialEq)] @@ -31,62 +44,104 @@ pub enum SingleSignerError { #[error("the protocol signer creation failed: `{0}`")] ProtocolSignerCreationFailure(String), + + #[error("codec error: '{0}'")] + Codec(String), } pub struct MithrilSingleSigner { party_id: u64, - secret_key: ProtocolSignerSecretKey, + protocol_initializer: Option, + is_registered: bool, } impl MithrilSingleSigner { - pub fn new(party_id: u64, secret_key: ProtocolSignerSecretKey) -> Self { + pub fn new(party_id: u64, protocol_initializer_encoded: &str) -> Self { + let protocol_initializer = key_decode_hex(protocol_initializer_encoded).ok(); Self { party_id, - secret_key, + protocol_initializer, + is_registered: false, } } - pub fn create_protocol_signer( - &self, + fn create_protocol_signer( + &mut self, current_player_stake: ProtocolStake, - stake_distribution: &[SignerWithStake], // @todo : use a hmap to prevent party id duplication + stake_distribution: &[SignerWithStake], // TODO : use a hmap to prevent party id duplication protocol_parameters: &entities::ProtocolParameters, - ) -> Result { + ) -> Result { let players = stake_distribution .iter() .map(|s| (s.party_id as ProtocolPartyId, s.stake as ProtocolStake)) .collect::>(); - let protocol_parameters = ProtocolParameters { - k: protocol_parameters.k, - m: protocol_parameters.m, - phi_f: protocol_parameters.phi_f as f64, + + let protocol_initializer = match &self.protocol_initializer { + None => { + let mut rng = rand_core::OsRng; + ProtocolInitializer::setup( + protocol_parameters.to_owned().into(), + self.party_id as ProtocolPartyId, + current_player_stake, + &mut rng, + ) + } + Some(protocol_initializer) => protocol_initializer.to_owned(), }; + self.protocol_initializer = Some(protocol_initializer); let mut key_reg = ProtocolKeyRegistration::init(&players); - for s in stake_distribution { - let decoded_key = key_decode_hex(&s.verification_key)?; - key_reg - .register(s.party_id as ProtocolPartyId, decoded_key) - .unwrap(); - } - let closed_reg = key_reg.close(); - - let mut rng = rand_core::OsRng; - let mut initializer = ProtocolInitializer::setup( - protocol_parameters, - self.party_id as ProtocolPartyId, - current_player_stake, - &mut rng, - ); - initializer.set_key(self.secret_key.clone()); + let signers = stake_distribution + .iter() + .filter(|signer| !signer.verification_key.is_empty()) + .collect::>(); - Ok(initializer.new_signer(closed_reg)) + match signers.len() { + 0 => Err(SingleSignerError::ProtocolSignerCreationFailure( + "no signer".to_string(), + )), + _ => { + for s in signers { + let decoded_key = + key_decode_hex(&s.verification_key).map_err(SingleSignerError::Codec)?; + key_reg + .register(s.party_id as ProtocolPartyId, decoded_key) + .map_err(|e| { + SingleSignerError::ProtocolSignerCreationFailure(e.to_string()) + })?; + } + let closed_reg = key_reg.close(); + Ok(self + .protocol_initializer + .as_ref() + .unwrap() + .clone() + .new_signer(closed_reg)) + } + } } } impl SingleSigner for MithrilSingleSigner { + fn get_party_id(&self) -> ProtocolPartyId { + self.party_id + } + + fn get_protocol_initializer(&self) -> Option { + self.protocol_initializer.clone() + } + + fn get_is_registered(&self) -> bool { + self.is_registered + } + + fn update_is_registered(&mut self, is_registered: bool) -> Result<(), SingleSignerError> { + self.is_registered = is_registered; + Ok(()) + } + fn compute_single_signatures( - &self, + &mut self, message: Bytes, stake_distribution: Vec, // TODO : use a hmap to prevent party id duplication protocol_parameters: &entities::ProtocolParameters, @@ -96,17 +151,19 @@ impl SingleSigner for MithrilSingleSigner { .find(|s| s.party_id == self.party_id) .ok_or(SingleSignerError::UnregisteredPartyId())?; - if current_signer_with_stake.verification_key.is_empty() { - return Err(SingleSignerError::UnregisteredVerificationKey()); + if let Some(protocol_initializer) = self.get_protocol_initializer() { + let verification_key = key_encode_hex(protocol_initializer.verification_key()) + .map_err(SingleSignerError::Codec)?; + if current_signer_with_stake.verification_key != verification_key { + return Err(SingleSignerError::UnregisteredVerificationKey()); + } } - let protocol_signer = self - .create_protocol_signer( - current_signer_with_stake.stake, - &stake_distribution, - protocol_parameters, - ) - .map_err(SingleSignerError::ProtocolSignerCreationFailure)?; + let protocol_signer = self.create_protocol_signer( + current_signer_with_stake.stake, + &stake_distribution, + protocol_parameters, + )?; trace!( "Party #{}: sign message {}", @@ -121,7 +178,7 @@ impl SingleSigner for MithrilSingleSigner { let encoded_signature = key_encode_hex(&signature); if encoded_signature.is_err() { - warn!("couldn't compute signature: `{:?}`", encoded_signature); // @todo: structured log + warn!("couldn't compute signature: `{:?}`", encoded_signature); // TODO: structured log continue; } @@ -140,38 +197,40 @@ impl SingleSigner for MithrilSingleSigner { mod tests { use super::*; - use mithril_common::crypto_helper::{key_decode_hex, ProtocolClerk, ProtocolSigner}; - use mithril_common::fake_data; + use mithril_common::crypto_helper::tests_setup::*; + use mithril_common::crypto_helper::{key_decode_hex, ProtocolClerk}; #[test] fn cant_compute_if_signer_verification_key_is_not_registered() { - let signer_with_keys = fake_data::signer_keys(0).unwrap(); - let single_signer = MithrilSingleSigner::new( - signer_with_keys.party_id, - key_decode_hex(&signer_with_keys.secret_key).unwrap(), + let message = setup_message(); + let protocol_parameters = setup_protocol_parameters(); + let signers = setup_signers(5); + let signer_unregistered = &signers[4]; + let stake_distribution = signers[..4] + .iter() + .map( + |(party_id, stake, verification_key, _protocol_signer, _protocol_initializer)| { + let verification_key = match party_id { + 0 => key_encode_hex(signer_unregistered.2).unwrap(), + _ => key_encode_hex(verification_key).unwrap(), + }; + SignerWithStake::new(*party_id, verification_key, *stake) + }, + ) + .collect::>(); + let current_signer = &signers[0]; + let mut single_signer = MithrilSingleSigner::new( + current_signer.0, + &key_encode_hex(¤t_signer.4).unwrap(), ); - let stake_distribution = fake_data::signers_with_stakes(5) - .into_iter() - .map(|s| { - if s.party_id == signer_with_keys.party_id { - SignerWithStake { - party_id: s.party_id, - verification_key: "".to_string(), - stake: s.stake, - } - } else { - s - } - }) - .collect(); - let protocol_parameters = fake_data::protocol_parameters(); let sign_result = single_signer.compute_single_signatures( - "message".as_bytes().to_vec(), + message.clone(), stake_distribution, - &protocol_parameters, + &protocol_parameters.into(), ); + assert!(&single_signer.get_protocol_initializer().is_some()); assert_eq!( SingleSignerError::UnregisteredVerificationKey(), sign_result.unwrap_err() @@ -180,37 +239,41 @@ mod tests { #[test] fn should_produce_a_single_signature() { - let signer_with_keys = fake_data::signer_keys(0).unwrap(); - let single_signer = MithrilSingleSigner::new( - signer_with_keys.party_id, - key_decode_hex(&signer_with_keys.secret_key).unwrap(), - ); - let stake_distribution = fake_data::signers_with_stakes(5); - let protocol_parameters = fake_data::protocol_parameters(); - let protocol_signer: ProtocolSigner = single_signer - .create_protocol_signer( - signer_with_keys.stake, - &stake_distribution, - &protocol_parameters, + let message = setup_message(); + let protocol_parameters = setup_protocol_parameters(); + let signers = setup_signers(5); + let stake_distribution = signers + .iter() + .map( + |(party_id, stake, verification_key, _protocol_signer, _protocol_initializer)| { + SignerWithStake::new( + *party_id, + key_encode_hex(verification_key).unwrap(), + *stake, + ) + }, ) - .unwrap(); - let clerk = ProtocolClerk::from_signer(&protocol_signer); + .collect::>(); + let current_signer = &signers[0]; + let mut single_signer = MithrilSingleSigner::new( + current_signer.0, + &key_encode_hex(¤t_signer.4).unwrap(), + ); - let message = "message".as_bytes(); let sign_result = single_signer.compute_single_signatures( - message.to_vec(), + message.clone(), stake_distribution, - &protocol_parameters, + &protocol_parameters.into(), ); + let protocol_signer = ¤t_signer.3; + let clerk = ProtocolClerk::from_signer(&protocol_signer); + + assert!(&single_signer.get_protocol_initializer().is_some()); assert!(!sign_result.as_ref().unwrap().is_empty()); for sig in sign_result.unwrap() { let decoded_sig = key_decode_hex(&sig.signature).unwrap(); - assert!(clerk.verify_sig(&decoded_sig, message).is_ok()); - assert_eq!( - decoded_sig.pk, - key_decode_hex(&signer_with_keys.verification_key).unwrap() - ); + assert!(clerk.verify_sig(&decoded_sig, &message).is_ok()); } } }