From de2844fa9a18e7f0c7c1595a480ea1a2c3a48b28 Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Mon, 18 Dec 2023 14:41:45 -0500 Subject: [PATCH] Process coordinator messages to duplicate state between multiple coordinators Signed-off-by: Jacinta Ferrant --- stacks-signer/src/runloop.rs | 128 ++---------------------- testnet/stacks-node/src/tests/signer.rs | 66 ++++++------ 2 files changed, 43 insertions(+), 151 deletions(-) diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index e4a6e5c01b2..c3cbe0dba9c 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -2,13 +2,12 @@ use std::collections::VecDeque; use std::sync::mpsc::Sender; use std::time::Duration; -use backoff::default; use libsigner::{SignerRunLoop, StackerDBChunksEvent}; use slog::{slog_debug, slog_error, slog_info, slog_warn}; use stacks_common::{debug, error, info, warn}; use wsts::common::MerkleRoot; use wsts::curve::ecdsa; -use wsts::net::{Message, Packet, Signable}; +use wsts::net::Packet; use wsts::state_machine::coordinator::frost::Coordinator as FrostCoordinator; use wsts::state_machine::coordinator::{Config as CoordinatorConfig, Coordinator}; use wsts::state_machine::signer::Signer; @@ -170,7 +169,7 @@ impl RunLoop { event: &StackerDBChunksEvent, ) -> (Vec, Vec) { // Determine the current coordinator id and public key for verification - let (coordinator_id, coordinator_public_key) = + let (_coordinator_id, coordinator_public_key) = calculate_coordinator(&self.signing_round.public_keys); // Filter out invalid messages let inbound_messages: Vec = event @@ -178,11 +177,7 @@ impl RunLoop { .iter() .filter_map(|chunk| { let message = bincode::deserialize::(&chunk.data).ok()?; - if verify_msg( - &message, - &self.signing_round.public_keys, - coordinator_public_key, - ) { + if message.verify(&self.signing_round.public_keys, coordinator_public_key) { Some(message) } else { None @@ -194,14 +189,11 @@ impl RunLoop { .signing_round .process_inbound_messages(&inbound_messages) .unwrap_or_default(); - // If the signer is the coordinator, then next process the message as the coordinator - let (messages, results) = if self.signing_round.signer_id == coordinator_id { - self.coordinator - .process_inbound_messages(&inbound_messages) - .unwrap_or_default() - } else { - (vec![], vec![]) - }; + // Next process the message as the coordinator + let (messages, results) = self + .coordinator + .process_inbound_messages(&inbound_messages) + .unwrap_or_default(); outbound_messages.extend(messages); (outbound_messages, results) } @@ -334,107 +326,3 @@ fn calculate_coordinator(public_keys: &PublicKeys) -> (u32, &ecdsa::PublicKey) { // Mockamato just uses the first signer_id as the coordinator for now (0, public_keys.signers.get(&0).unwrap()) } - -/// TODO: this should not be here. -/// Temporary copy paste from frost-signer -/// See: https://github.com/stacks-network/stacks-blockchain/issues/3913 -fn verify_msg( - m: &Packet, - public_keys: &PublicKeys, - coordinator_public_key: &ecdsa::PublicKey, -) -> bool { - match &m.msg { - Message::DkgBegin(msg) | Message::DkgPrivateBegin(msg) => { - if !msg.verify(&m.sig, coordinator_public_key) { - warn!("Received a DkgPrivateBegin message with an invalid signature."); - return false; - } - } - Message::DkgEnd(msg) => { - if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { - if !msg.verify(&m.sig, public_key) { - warn!("Received a DkgPublicEnd message with an invalid signature."); - return false; - } - } else { - warn!( - "Received a DkgPublicEnd message with an unknown id: {}", - msg.signer_id - ); - return false; - } - } - Message::DkgPublicShares(msg) => { - if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { - if !msg.verify(&m.sig, public_key) { - warn!("Received a DkgPublicShares message with an invalid signature."); - return false; - } - } else { - warn!( - "Received a DkgPublicShares message with an unknown id: {}", - msg.signer_id - ); - return false; - } - } - Message::DkgPrivateShares(msg) => { - // Private shares have key IDs from [0, N) to reference IDs from [1, N] - // in Frost V4 to enable easy indexing hence ID + 1 - // TODO: Once Frost V5 is released, this off by one adjustment will no longer be required - if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { - if !msg.verify(&m.sig, public_key) { - warn!("Received a DkgPrivateShares message with an invalid signature from signer_id {} key {}", msg.signer_id, &public_key); - return false; - } - } else { - warn!( - "Received a DkgPrivateShares message with an unknown id: {}", - msg.signer_id - ); - return false; - } - } - Message::NonceRequest(msg) => { - if !msg.verify(&m.sig, coordinator_public_key) { - warn!("Received a NonceRequest message with an invalid signature."); - return false; - } - } - Message::NonceResponse(msg) => { - if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { - if !msg.verify(&m.sig, public_key) { - warn!("Received a NonceResponse message with an invalid signature."); - return false; - } - } else { - warn!( - "Received a NonceResponse message with an unknown id: {}", - msg.signer_id - ); - return false; - } - } - Message::SignatureShareRequest(msg) => { - if !msg.verify(&m.sig, coordinator_public_key) { - warn!("Received a SignatureShareRequest message with an invalid signature."); - return false; - } - } - Message::SignatureShareResponse(msg) => { - if let Some(public_key) = public_keys.signers.get(&msg.signer_id) { - if !msg.verify(&m.sig, public_key) { - warn!("Received a SignatureShareResponse message with an invalid signature."); - return false; - } - } else { - warn!( - "Received a SignatureShareResponse message with an unknown id: {}", - msg.signer_id - ); - return false; - } - } - } - true -} diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index c7256c5abbb..8afb9966f97 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -201,8 +201,8 @@ fn test_stackerdb_dkg() { .init(); // Generate Signer Data - let num_signers: u32 = 10; - let num_keys: u32 = 400; + let num_signers: u32 = 3; + let num_keys: u32 = 2; let publisher_private_key = StacksPrivateKey::new(); let signer_stacks_private_keys = (0..num_signers) .map(|_| StacksPrivateKey::new()) @@ -240,7 +240,7 @@ fn test_stackerdb_dkg() { let mut running_signers = vec![]; // Spawn all the signers first to listen to the coordinator request for dkg let mut signer_cmd_senders = Vec::new(); - let mut signer_res_receivers = Vec::new(); + let mut res_receivers = Vec::new(); for i in (1..num_signers).rev() { let (cmd_send, cmd_recv) = channel(); let (res_send, res_recv) = channel(); @@ -248,7 +248,7 @@ fn test_stackerdb_dkg() { let running_signer = spawn_signer(&signer_configs[i as usize], cmd_recv, res_send); running_signers.push(running_signer); signer_cmd_senders.push(cmd_send); - signer_res_receivers.push(res_recv); + res_receivers.push(res_recv); } // Spawn coordinator second let (coordinator_cmd_send, coordinator_cmd_recv) = channel(); @@ -260,6 +260,8 @@ fn test_stackerdb_dkg() { coordinator_res_send, ); + res_receivers.push(coordinator_res_recv); + // Let's wrap the node in a lifetime to ensure stopping the signers doesn't cause issues. { // Setup the nodes and deploy the contract to it @@ -291,35 +293,37 @@ fn test_stackerdb_dkg() { merkle_root: None, }) .expect("failed to send Sign command"); - - let mut aggregate_group_key = None; - let mut frost_signature = None; - let mut schnorr_proof = None; - - loop { - let results = coordinator_res_recv.recv().expect("failed to recv results"); - for result in results { - match result { - OperationResult::Dkg(point) => { - info!("Received aggregate_group_key {point}"); - aggregate_group_key = Some(point); - } - OperationResult::Sign(sig) => { - info!("Received Signature ({},{})", &sig.R, &sig.z); - frost_signature = Some(sig); - } - OperationResult::SignTaproot(proof) => { - info!("Received SchnorrProof ({},{})", &proof.r, &proof.s); - schnorr_proof = Some(proof); - } - OperationResult::DkgError(..) | OperationResult::SignError(..) => { - todo!() + for recv in res_receivers.iter() { + let mut aggregate_group_key = None; + let mut frost_signature = None; + let mut schnorr_proof = None; + loop { + let results = recv.recv().expect("failed to recv results"); + for result in results { + match result { + OperationResult::Dkg(point) => { + info!("Received aggregate_group_key {point}"); + aggregate_group_key = Some(point); + } + OperationResult::Sign(sig) => { + info!("Received Signature ({},{})", &sig.R, &sig.z); + frost_signature = Some(sig); + } + OperationResult::SignTaproot(proof) => { + info!("Received SchnorrProof ({},{})", &proof.r, &proof.s); + schnorr_proof = Some(proof); + } + OperationResult::DkgError(..) | OperationResult::SignError(..) => { + todo!() + } } } - } - if aggregate_group_key.is_some() && frost_signature.is_some() && schnorr_proof.is_some() - { - break; + if aggregate_group_key.is_some() + && frost_signature.is_some() + && schnorr_proof.is_some() + { + break; + } } } let elapsed = now.elapsed();