Skip to content

Commit

Permalink
Process coordinator messages to duplicate state between multiple coor…
Browse files Browse the repository at this point in the history
…dinators

Signed-off-by: Jacinta Ferrant <[email protected]>
  • Loading branch information
jferrant committed Dec 18, 2023
1 parent 8cffc38 commit de2844f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 151 deletions.
128 changes: 8 additions & 120 deletions stacks-signer/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,19 +169,15 @@ impl<C: Coordinator> RunLoop<C> {
event: &StackerDBChunksEvent,
) -> (Vec<Packet>, Vec<OperationResult>) {
// 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<Packet> = event
.modified_slots
.iter()
.filter_map(|chunk| {
let message = bincode::deserialize::<Packet>(&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
Expand All @@ -194,14 +189,11 @@ impl<C: Coordinator> RunLoop<C> {
.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)
}
Expand Down Expand Up @@ -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
}
66 changes: 35 additions & 31 deletions testnet/stacks-node/src/tests/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -240,15 +240,15 @@ 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();
info!("spawn signer");
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();
Expand All @@ -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
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit de2844f

Please sign in to comment.