From 66902decea73b9fb2aeeeb94c6bec8767e5fa30b Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Thu, 28 Sep 2023 17:11:25 -0400 Subject: [PATCH 1/6] remove stacks-sbtc dependency; use wsts net and state_machine mods instead --- Cargo.lock | 265 ++--------------------------- stacks-signer/Cargo.toml | 4 +- stacks-signer/src/config.rs | 6 +- stacks-signer/src/crypto/frost.rs | 104 ++++++----- stacks-signer/src/crypto/mod.rs | 38 ----- stacks-signer/src/lib.rs | 2 - stacks-signer/src/main.rs | 2 +- stacks-signer/src/runloop.rs | 132 +++++--------- stacks-signer/src/stacks_client.rs | 25 ++- 9 files changed, 121 insertions(+), 457 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45903ffa7..479854765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,17 +391,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "getrandom 0.2.8", - "instant", - "rand 0.8.5", -] - [[package]] name = "backtrace" version = "0.3.67" @@ -412,7 +401,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.6.2", + "miniz_oxide", "object", "rustc-demangle", ] @@ -845,7 +834,7 @@ dependencies = [ "clap 2.34.0", "criterion-plot", "csv", - "itertools 0.10.5", + "itertools", "lazy_static", "num-traits", "oorandom", @@ -867,7 +856,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", ] [[package]] @@ -1214,16 +1203,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "flate2" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" -dependencies = [ - "crc32fast", - "miniz_oxide 0.7.1", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1239,47 +1218,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "frost-coordinator" -version = "0.0.1" -source = "git+https://github.com/Trust-Machines/stacks-sbtc#dbd060b99f9cdaf2c06d26d60da1f12744f1d600" -dependencies = [ - "backoff", - "clap 4.4.1", - "frost-signer", - "hashbrown 0.14.0", - "p256k1", - "serde", - "thiserror", - "tracing", - "tracing-subscriber", - "wsts", -] - -[[package]] -name = "frost-signer" -version = "0.0.1" -source = "git+https://github.com/Trust-Machines/stacks-sbtc#dbd060b99f9cdaf2c06d26d60da1f12744f1d600" -dependencies = [ - "aes-gcm 0.10.2", - "backoff", - "bincode", - "clap 4.4.1", - "hashbrown 0.14.0", - "itertools 0.11.0", - "p256k1", - "rand 0.8.5", - "rand_core 0.6.4", - "serde", - "sha2 0.10.6", - "thiserror", - "toml 0.7.6", - "tracing", - "tracing-subscriber", - "ureq", - "wsts", -] - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1736,7 +1674,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.7", + "rustls", "tokio", "tokio-rustls", ] @@ -1883,15 +1821,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.6" @@ -2053,15 +1982,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - [[package]] name = "memchr" version = "2.5.0" @@ -2117,15 +2037,6 @@ dependencies = [ "adler", ] -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - [[package]] name = "mio" version = "0.6.23" @@ -2221,16 +2132,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi 0.3.9", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -2302,12 +2203,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p256k1" version = "5.4.1" @@ -2319,7 +2214,7 @@ dependencies = [ "bs58 0.4.0", "cc", "hex", - "itertools 0.10.5", + "itertools", "num-traits", "primitive-types", "proc-macro2", @@ -2758,15 +2653,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - [[package]] name = "regex-syntax" version = "0.6.28" @@ -2796,7 +2682,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.7", + "rustls", "rustls-pemfile", "serde", "serde_json", @@ -2808,7 +2694,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.2", + "webpki-roots", "winreg", ] @@ -2975,7 +2861,7 @@ dependencies = [ "serde", "tempfile", "thiserror", - "toml 0.5.11", + "toml", "toolchain_find", ] @@ -2993,18 +2879,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "rustls" version = "0.21.7" @@ -3195,15 +3069,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - [[package]] name = "serde_stacker" version = "0.1.8" @@ -3317,15 +3182,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.1.0" @@ -3489,7 +3345,7 @@ dependencies = [ "stackslib", "stx-genesis", "tokio", - "toml 0.5.11", + "toml", "warp", ] @@ -3500,8 +3356,6 @@ dependencies = [ "bincode", "clap 4.4.1", "clarity", - "frost-coordinator", - "frost-signer", "hashbrown 0.14.0", "libsigner", "libstackerdb", @@ -3517,7 +3371,7 @@ dependencies = [ "slog-term", "stacks-common", "thiserror", - "toml 0.5.11", + "toml", "wsts", ] @@ -3894,7 +3748,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.7", + "rustls", "tokio", ] @@ -3944,26 +3798,11 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -3972,8 +3811,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -4028,36 +3865,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -4171,24 +3978,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "ureq" -version = "2.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" -dependencies = [ - "base64 0.13.1", - "flate2", - "log", - "once_cell", - "rustls 0.20.8", - "serde", - "serde_json", - "url", - "webpki", - "webpki-roots 0.22.6", -] - [[package]] name = "url" version = "2.3.1" @@ -4213,12 +4002,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "value-bag" version = "1.0.0-alpha.9" @@ -4393,25 +4176,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - [[package]] name = "webpki-roots" version = "0.25.2" @@ -4650,10 +4414,10 @@ dependencies = [ [[package]] name = "wsts" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9822f9052b53d23fb9535781ee904e444cb5292788ae2f08e08de27eb765b39c" +version = "4.0.0" +source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#e3d80a9b9d1d7a75ad3b0c95bc75964b00c9de39" dependencies = [ + "aes-gcm 0.10.2", "bs58 0.5.0", "hashbrown 0.14.0", "hex", @@ -4665,6 +4429,7 @@ dependencies = [ "serde", "sha2 0.10.6", "thiserror", + "tracing", ] [[package]] diff --git a/stacks-signer/Cargo.toml b/stacks-signer/Cargo.toml index fa24a7b61..50211c3eb 100644 --- a/stacks-signer/Cargo.toml +++ b/stacks-signer/Cargo.toml @@ -23,8 +23,6 @@ path = "src/main.rs" bincode = "1.3.3" clarity = { path = "../clarity" } clap = { version = "4.1.1", features = ["derive", "env"] } -frost-coordinator = { git = "https://github.com/Trust-Machines/stacks-sbtc" } -frost-signer = { git = "https://github.com/Trust-Machines/stacks-sbtc" } hashbrown = "0.14" libsigner = { path = "../libsigner" } libstackerdb = { path = "../libstackerdb" } @@ -39,7 +37,7 @@ slog-term = "2.6.0" stacks-common = { path = "../stacks-common" } thiserror = "1.0" toml = "0.5.6" -wsts = "2.0" +wsts = { git = "https://github.com/Trust-Machines/wsts", branch = "add-network-types" } [dependencies.serde_json] version = "1.0" diff --git a/stacks-signer/src/config.rs b/stacks-signer/src/config.rs index 5bfe5d002..8bd556c85 100644 --- a/stacks-signer/src/config.rs +++ b/stacks-signer/src/config.rs @@ -15,7 +15,7 @@ // along with this program. If not, see . use clarity::vm::types::QualifiedContractIdentifier; -use frost_signer::config::{PublicKeys, SignerKeyIds}; +use hashbrown::HashMap; use p256k1::{ecdsa, scalar::Scalar}; use serde::Deserialize; use stacks_common::types::chainstate::StacksPrivateKey; @@ -26,6 +26,10 @@ use std::{ path::PathBuf, time::Duration, }; +use wsts::state_machine::PublicKeys; + +/// List of key_ids for each signer_id +pub type SignerKeyIds = HashMap>; const EVENT_TIMEOUT_MS: u64 = 5000; diff --git a/stacks-signer/src/crypto/frost.rs b/stacks-signer/src/crypto/frost.rs index a313cadaa..f1d3220ac 100644 --- a/stacks-signer/src/crypto/frost.rs +++ b/stacks-signer/src/crypto/frost.rs @@ -1,21 +1,21 @@ -use std::collections::BTreeMap; - -use crate::crypto::{Coordinatable, Error as CryptoError, OperationResult}; -use frost_signer::{ - net::Message, - signing_round::{ - DkgBegin, DkgPublicShare, MessageTypes, NonceRequest, NonceResponse, Signable, - SignatureShareRequest, - }, -}; use hashbrown::HashSet; use slog::{slog_info, slog_warn}; use stacks_common::{error, info, warn}; +use std::collections::BTreeMap; use wsts::{ common::{PolyCommitment, PublicNonce, Signature, SignatureShare}, compute, errors::AggregatorError, - taproot::{Error as TaprootError, SchnorrProof}, + net::{ + DkgBegin, DkgPublicShares, NonceRequest, NonceResponse, Signable, SignatureShareRequest, Message, Packet, + }, + state_machine::{ + coordinator::{ + Coordinatable, Coordinator, + } + OperationResult, + }, + taproot::SchnorrProof, v1, Point, Scalar, }; @@ -40,9 +40,6 @@ pub enum Error { /// SignatureAggregator error #[error("Aggregator: {0}")] Aggregator(AggregatorError), - /// Taproot error - #[error("Taproot")] - Taproot(TaprootError), /// Schnorr proof failed to verify #[error("Schnorr Proof failed to verify")] SchnorrProofFailed, @@ -54,11 +51,6 @@ impl From for Error { } } -impl From for Error { - fn from(err: TaprootError) -> Self { - Error::Taproot(err) - } -} /// The coordinator for the FROST algorithm pub struct Coordinator { @@ -121,8 +113,8 @@ impl Coordinator { /// Handle a message from the stacker-db instance pub fn process_message( &mut self, - message: &Message, - ) -> Result<(Option, Option), Error> { + message: &Packet, + ) -> Result<(Option, Option), Error> { loop { match self.state { State::Idle => { @@ -197,7 +189,7 @@ impl Coordinator { } /// Start a DKG round - pub fn start_dkg_round(&mut self) -> Result { + pub fn start_dkg_round(&mut self) -> Result { self.current_dkg_id = self.current_dkg_id.wrapping_add(1); info!("Starting DKG round #{}", self.current_dkg_id); self.move_to(State::DkgPublicDistribute)?; @@ -205,14 +197,14 @@ impl Coordinator { } /// Start a signing round - pub fn start_signing_round(&mut self) -> Result { + pub fn start_signing_round(&mut self) -> Result { self.current_sign_id = self.current_sign_id.wrapping_add(1); info!("Starting signing round #{}", self.current_sign_id); self.move_to(State::NonceRequest)?; self.request_nonces() } - fn start_public_shares(&mut self) -> Result { + fn start_public_shares(&mut self) -> Result { self.dkg_public_shares.clear(); info!( "DKG Round #{}: Starting Public Share Distribution Round #{}", @@ -222,15 +214,15 @@ impl Coordinator { dkg_id: self.current_dkg_id, }; - let dkg_begin_message = Message { + let dkg_begin_message = Packet { sig: dkg_begin.sign(&self.message_private_key).expect(""), - msg: MessageTypes::DkgBegin(dkg_begin), + msg: Message::DkgBegin(dkg_begin), }; self.move_to(State::DkgPublicGather)?; Ok(dkg_begin_message) } - fn start_private_shares(&mut self) -> Result { + fn start_private_shares(&mut self) -> Result { info!( "DKG Round #{}: Starting Private Share Distribution", self.current_dkg_id @@ -238,17 +230,17 @@ impl Coordinator { let dkg_begin = DkgBegin { dkg_id: self.current_dkg_id, }; - let dkg_private_begin_msg = Message { + let dkg_private_begin_msg = Packet { sig: dkg_begin.sign(&self.message_private_key).expect(""), - msg: MessageTypes::DkgPrivateBegin(dkg_begin), + msg: Message::DkgPrivateBegin(dkg_begin), }; self.move_to(State::DkgEndGather)?; Ok(dkg_private_begin_msg) } - fn gather_public_shares(&mut self, message: &Message) -> Result<(), Error> { + fn gather_public_shares(&mut self, message: &Packet) -> Result<(), Error> { match &message.msg { - MessageTypes::DkgPublicEnd(dkg_end) => { + Message::DkgPublicEnd(dkg_end) => { if dkg_end.dkg_id != self.current_dkg_id { return Err(Error::BadDkgId(dkg_end.dkg_id, self.current_dkg_id)); } @@ -258,7 +250,7 @@ impl Coordinator { dkg_end.dkg_id, dkg_end.signer_id, self.ids_to_await ); } - MessageTypes::DkgPublicShare(dkg_public_share) => { + Message::DkgPublicShare(dkg_public_share) => { if dkg_public_share.dkg_id != self.current_dkg_id { return Err(Error::BadDkgId( dkg_public_share.dkg_id, @@ -306,12 +298,12 @@ impl Coordinator { Ok(()) } - fn gather_dkg_end(&mut self, message: &Message) -> Result<(), Error> { + fn gather_dkg_end(&mut self, message: &Packet) -> Result<(), Error> { info!( "DKG Round #{}: waiting for Dkg End from signers {:?}", self.current_dkg_id, self.ids_to_await ); - if let MessageTypes::DkgEnd(dkg_end) = &message.msg { + if let Message::DkgEnd(dkg_end) = &message.msg { if dkg_end.dkg_id != self.current_dkg_id { return Err(Error::BadDkgId(dkg_end.dkg_id, self.current_dkg_id)); } @@ -329,7 +321,7 @@ impl Coordinator { Ok(()) } - fn request_nonces(&mut self) -> Result { + fn request_nonces(&mut self) -> Result { info!( "Sign Round #{} Nonce round #{} Requesting Nonces", self.current_sign_id, self.current_sign_nonce_id, @@ -339,17 +331,17 @@ impl Coordinator { sign_id: self.current_sign_id, sign_nonce_id: self.current_sign_nonce_id, }; - let nonce_request_msg = Message { + let nonce_request_msg = Packet { sig: nonce_request.sign(&self.message_private_key).expect(""), - msg: MessageTypes::NonceRequest(nonce_request), + msg: Message::NonceRequest(nonce_request), }; self.ids_to_await = (0..self.total_signers).collect(); self.move_to(State::NonceGather)?; Ok(nonce_request_msg) } - fn gather_nonces(&mut self, message: &Message) -> Result<(), Error> { - if let MessageTypes::NonceResponse(nonce_response) = &message.msg { + fn gather_nonces(&mut self, message: &Packet) -> Result<(), Error> { + if let Message::NonceResponse(nonce_response) = &message.msg { if nonce_response.dkg_id != self.current_dkg_id { return Err(Error::BadDkgId(nonce_response.dkg_id, self.current_dkg_id)); } @@ -395,7 +387,7 @@ impl Coordinator { Ok(()) } - fn request_sig_shares(&mut self) -> Result { + fn request_sig_shares(&mut self) -> Result { info!( "Sign Round #{} Requesting Signature Shares", self.current_sign_id, @@ -410,17 +402,17 @@ impl Coordinator { nonce_responses, message: self.message.clone(), }; - let sig_share_request_msg = Message { + let sig_share_request_msg = Packet { sig: sig_share_request.sign(&self.message_private_key).expect(""), - msg: MessageTypes::SignShareRequest(sig_share_request), // TODO: either SigShare or SignatureShare, SignShare is right out + msg: Message::SignShareRequest(sig_share_request), // TODO: either SigShare or SignatureShare, SignShare is right out }; self.ids_to_await = (0..self.total_signers).collect(); self.move_to(State::SigShareGather)?; Ok(sig_share_request_msg) } - fn gather_sig_shares(&mut self, message: &Message) -> Result<(), Error> { - if let MessageTypes::SignShareResponse(sig_share_response) = &message.msg { + fn gather_sig_shares(&mut self, message: &Packet) -> Result<(), Error> { + if let Message::SignShareResponse(sig_share_response) = &message.msg { if sig_share_response.dkg_id != self.current_dkg_id { return Err(Error::BadDkgId( sig_share_response.dkg_id, @@ -474,7 +466,9 @@ impl Coordinator { ); let mut aggregator = - v1::SignatureAggregator::new(self.total_keys, self.threshold, polys)?; + v1::Aggregator::new(self.total_keys, self.threshold); + + aggregator.init(polys)?; let sig = aggregator.sign(&self.message, &nonces, shares)?; @@ -594,8 +588,8 @@ impl Coordinatable for Coordinator { /// Process inbound messages fn process_inbound_messages( &mut self, - messages: Vec, - ) -> Result<(Vec, Vec), crate::crypto::Error> { + messages: Vec, + ) -> Result<(Vec, Vec), crate::crypto::Error> { let mut outbound_messages = vec![]; let mut operation_results = vec![]; for message in &messages { @@ -616,13 +610,13 @@ impl Coordinatable for Coordinator { } /// Trigger a DKG round - fn start_distributed_key_generation(&mut self) -> Result { + fn start_distributed_key_generation(&mut self) -> Result { let message = self.start_dkg_round()?; Ok(message) } // Trigger a signing round - fn start_signing_message(&mut self, message: &[u8]) -> Result { + fn start_signing_message(&mut self, message: &[u8]) -> Result { self.message = message.to_vec(); let message = self.start_signing_round()?; Ok(message) @@ -730,7 +724,7 @@ mod test { let result = coordinator.start_dkg_round(); assert!(result.is_ok()); - assert!(matches!(result.unwrap().msg, MessageTypes::DkgBegin(_))); + assert!(matches!(result.unwrap().msg, Message::DkgBegin(_))); assert_eq!(coordinator.state, State::DkgPublicGather); assert_eq!(coordinator.current_dkg_id, 1); } @@ -748,7 +742,7 @@ mod test { let result = coordinator.start_public_shares().unwrap(); - assert!(matches!(result.msg, MessageTypes::DkgBegin(_))); + assert!(matches!(result.msg, Message::DkgBegin(_))); assert_eq!(coordinator.state, State::DkgPublicGather); assert_eq!(coordinator.current_dkg_id, 0); } @@ -765,7 +759,7 @@ mod test { coordinator.state = State::DkgPrivateDistribute; // Must be in this state before calling start private shares let message = coordinator.start_private_shares().unwrap(); - assert!(matches!(message.msg, MessageTypes::DkgPrivateBegin(_))); + assert!(matches!(message.msg, Message::DkgPrivateBegin(_))); assert_eq!(coordinator.state, State::DkgEndGather); assert_eq!(coordinator.current_dkg_id, 0); } @@ -824,8 +818,8 @@ mod test { fn feedback_messages( coordinator: &mut Coordinator, signing_rounds: &mut Vec, - messages: Vec, - ) -> (Vec, Vec) { + messages: Vec, + ) -> (Vec, Vec) { let mut inbound_messages = vec![]; let mut feedback_messages = vec![]; for signing_round in signing_rounds.as_mut_slice() { @@ -861,7 +855,7 @@ mod test { // Successfully got an Aggregate Public Key... assert_eq!(outbound_messages.len(), 1); match &outbound_messages[0].msg { - MessageTypes::DkgPrivateBegin(_) => {} + Message::DkgPrivateBegin(_) => {} _ => { panic!("Expected DkgPrivateBegin message"); } diff --git a/stacks-signer/src/crypto/mod.rs b/stacks-signer/src/crypto/mod.rs index 51d8c6ab4..fef2cd9e0 100644 --- a/stacks-signer/src/crypto/mod.rs +++ b/stacks-signer/src/crypto/mod.rs @@ -1,40 +1,2 @@ /// FROST (Fast Reliable Optimistic Secure Threshold) signature generation module pub mod frost; - -use frost::Error as FrostError; -use frost_signer::net::Message; -use wsts::{common::Signature, taproot::SchnorrProof, Point}; - -#[derive(thiserror::Error, Debug)] -/// Error type for the crypto module -pub enum Error { - /// An error occurred in the FROST module - #[error("An error occurred in the FROST module: {0}")] - FrostError(#[from] FrostError), -} - -/// Result of a DKG or sign operation -#[allow(dead_code)] -pub enum OperationResult { - /// The DKG result - Dkg(Point), - /// The sign result - Sign(Signature, SchnorrProof), -} - -/// Coordinatable trait for handling the coordination of DKG and sign messages -pub trait Coordinatable { - /// Process inbound messages - fn process_inbound_messages( - &mut self, - messages: Vec, - ) -> Result<(Vec, Vec), Error>; - /// Retrieve the aggregate public key - fn get_aggregate_public_key(&self) -> Point; - /// Trigger a DKG round - fn start_distributed_key_generation(&mut self) -> Result; - /// Trigger a signing round - fn start_signing_message(&mut self, _message: &[u8]) -> Result; - /// Reset internal state - fn reset(&mut self); -} diff --git a/stacks-signer/src/lib.rs b/stacks-signer/src/lib.rs index 7cc2f2071..2a14245b6 100644 --- a/stacks-signer/src/lib.rs +++ b/stacks-signer/src/lib.rs @@ -7,8 +7,6 @@ Usage documentation can be found in the [README](https://github.com/Trust-Machin pub mod cli; /// The configuration module for the signer pub mod config; -/// All crypto related modules -pub mod crypto; /// The primary runloop for the signer pub mod runloop; /// The signer client for communicating with stackerdb/stacks nodes diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 1ca7b8512..79f1f3967 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -45,7 +45,6 @@ use stacks_signer::{ RunDkgArgs, SignArgs, StackerDBArgs, }, config::{Config, Network}, - crypto::{frost::Coordinator as FrostCoordinator, OperationResult}, runloop::{RunLoop, RunLoopCommand}, utils::{build_signer_config_tomls, build_stackerdb_contract}, }; @@ -57,6 +56,7 @@ use std::{ sync::mpsc::{channel, Receiver, Sender}, time::Duration, }; +use wsts::state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}; struct SpawnedSigner { running_signer: RunningSigner>, diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 329dfb5c1..9fbd616ac 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -1,18 +1,16 @@ -use frost_signer::{ - config::PublicKeys, - net::Message, - signing_round::{MessageTypes, Signable, SigningRound}, -}; +use crate::{config::Config, stacks_client::StacksClient}; use libsigner::{SignerRunLoop, StackerDBChunksEvent}; use p256k1::ecdsa; use slog::{slog_debug, slog_error, slog_info, slog_warn}; use stacks_common::{debug, error, info, warn}; use std::{collections::VecDeque, sync::mpsc::Sender, time::Duration}; - -use crate::{ - config::Config, - crypto::{frost::Coordinator as FrostCoordinator, Coordinatable, OperationResult}, - stacks_client::StacksClient, +use wsts::{ + net::{Message, Packet, Signable}, + state_machine::{ + coordinator::{Coordinatable, Coordinator as FrostCoordinator}, + signer::SigningRound, + OperationResult, PublicKeys, + }, }; /// Which operation to perform @@ -67,7 +65,7 @@ impl RunLoop { Ok(msg) => { let ack = self .stacks_client - .send_message(self.signing_round.signer.signer_id, msg); + .send_message(self.signing_round.signer_id, msg); debug!("ACK: {:?}", ack); self.state = State::Dkg; true @@ -86,7 +84,7 @@ impl RunLoop { Ok(msg) => { let ack = self .stacks_client - .send_message(self.signing_round.signer.signer_id, msg); + .send_message(self.signing_round.signer_id, msg); debug!("ACK: {:?}", ack); self.state = State::Sign; true @@ -126,16 +124,16 @@ impl RunLoop { fn process_event( &mut self, event: &StackerDBChunksEvent, - ) -> (Vec, Vec) { + ) -> (Vec, Vec) { // Determine the current coordinator id and public key for verification let (coordinator_id, coordinator_public_key) = calculate_coordinator(&self.signing_round.public_keys); // Filter out invalid messages - let inbound_messages: Vec = event + let inbound_messages: Vec = event .modified_slots .iter() .filter_map(|chunk| { - let message = bincode::deserialize::(&chunk.data).ok()?; + let message = bincode::deserialize::(&chunk.data).ok()?; if verify_msg( &message, &self.signing_round.public_keys, @@ -148,11 +146,12 @@ impl RunLoop { }) .collect(); // First process all messages as a signer - let mut outbound_messages = - process_inbound_messages(&mut self.signing_round, inbound_messages.clone()) - .unwrap_or_default(); + let mut outbound_messages = self + .signing_round + .process_inbound_messages(inbound_messages.clone()) + .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.signer_id == coordinator_id { + let (messages, results) = if self.signing_round.signer_id == coordinator_id { self.coordinator .process_inbound_messages(inbound_messages) .unwrap_or_default() @@ -215,60 +214,6 @@ impl From<&Config> for RunLoop { } } -/// Process inbound messages using the frost_signer signing round mechanism -pub fn process_inbound_messages( - signing_round: &mut SigningRound, - messages: Vec, -) -> Result, frost_signer::signing_round::Error> { - let mut responses = vec![]; - for message in messages { - // TODO: this code was swiped from frost-signer. Expose it there so we don't have duplicate code - // See: https://github.com/stacks-network/stacks-blockchain/issues/3913 - let outbounds = signing_round.process(message.msg)?; - for out in outbounds { - let msg = Message { - msg: out.clone(), - sig: match out { - MessageTypes::DkgBegin(msg) | MessageTypes::DkgPrivateBegin(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign DkgBegin") - .to_vec(), - MessageTypes::DkgEnd(msg) | MessageTypes::DkgPublicEnd(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign DkgEnd") - .to_vec(), - MessageTypes::DkgPublicShare(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign DkgPublicShare") - .to_vec(), - MessageTypes::DkgPrivateShares(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign DkgPrivateShare") - .to_vec(), - MessageTypes::NonceRequest(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign NonceRequest") - .to_vec(), - MessageTypes::NonceResponse(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign NonceResponse") - .to_vec(), - MessageTypes::SignShareRequest(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign SignShareRequest") - .to_vec(), - MessageTypes::SignShareResponse(msg) => msg - .sign(&signing_round.network_private_key) - .expect("failed to sign SignShareResponse") - .to_vec(), - }, - }; - responses.push(msg); - } - } - Ok(responses) -} - impl SignerRunLoop, RunLoopCommand> for RunLoop { fn set_event_timeout(&mut self, timeout: Duration) { self.event_timeout = timeout; @@ -297,7 +242,7 @@ impl SignerRunLoop, RunLoopCommand> for R for msg in outbound_messages { let ack = self .stacks_client - .send_message(self.signing_round.signer.signer_id, msg); + .send_message(self.signing_round.signer_id, msg); if let Ok(ack) = ack { debug!("ACK: {:?}", ack); } else { @@ -336,18 +281,18 @@ fn calculate_coordinator(public_keys: &PublicKeys) -> (u32, &ecdsa::PublicKey) { /// Temporary copy paste from frost-signer /// See: https://github.com/stacks-network/stacks-blockchain/issues/3913 fn verify_msg( - m: &Message, + m: &Packet, public_keys: &PublicKeys, coordinator_public_key: &ecdsa::PublicKey, ) -> bool { match &m.msg { - MessageTypes::DkgBegin(msg) | MessageTypes::DkgPrivateBegin(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; } } - MessageTypes::DkgEnd(msg) | MessageTypes::DkgPublicEnd(msg) => { + 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."); @@ -361,45 +306,44 @@ fn verify_msg( return false; } } - MessageTypes::DkgPublicShare(msg) => { - if let Some(public_key) = public_keys.key_ids.get(&msg.party_id) { + 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 DkgPublicShare message with an invalid signature."); + warn!("Received a DkgPublicShares message with an invalid signature."); return false; } } else { warn!( - "Received a DkgPublicShare message with an unknown id: {}", - msg.party_id + "Received a DkgPublicShares message with an unknown id: {}", + msg.signer_id ); return false; } } - MessageTypes::DkgPrivateShares(msg) => { + 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 - let key_id = msg.key_id + 1; - if let Some(public_key) = public_keys.key_ids.get(&key_id) { + 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 key_id {} key {}", msg.key_id, &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: {}", - key_id + msg.signer_id ); return false; } } - MessageTypes::NonceRequest(msg) => { + Message::NonceRequest(msg) => { if !msg.verify(&m.sig, coordinator_public_key) { warn!("Received a NonceRequest message with an invalid signature."); return false; } } - MessageTypes::NonceResponse(msg) => { + 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."); @@ -413,21 +357,21 @@ fn verify_msg( return false; } } - MessageTypes::SignShareRequest(msg) => { + Message::SignatureShareRequest(msg) => { if !msg.verify(&m.sig, coordinator_public_key) { - warn!("Received a SignShareRequest message with an invalid signature."); + warn!("Received a SignatureShareRequest message with an invalid signature."); return false; } } - MessageTypes::SignShareResponse(msg) => { + 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 SignShareResponse message with an invalid signature."); + warn!("Received a SignatureShareResponse message with an invalid signature."); return false; } } else { warn!( - "Received a SignShareResponse message with an unknown id: {}", + "Received a SignatureShareResponse message with an unknown id: {}", msg.signer_id ); return false; diff --git a/stacks-signer/src/stacks_client.rs b/stacks-signer/src/stacks_client.rs index f555a3f99..68e1cb60a 100644 --- a/stacks-signer/src/stacks_client.rs +++ b/stacks-signer/src/stacks_client.rs @@ -1,10 +1,10 @@ use bincode::Error as BincodeError; -use frost_signer::{net::Message, signing_round::MessageTypes}; use hashbrown::HashMap; use libsigner::{RPCError, SignerSession, StackerDBSession}; use libstackerdb::{Error as StackerDBError, StackerDBChunkAckData, StackerDBChunkData}; use slog::{slog_debug, slog_warn}; use stacks_common::{debug, types::chainstate::StacksPrivateKey, warn}; +use wsts::net::{Message, Packet}; use crate::config::Config; @@ -59,7 +59,7 @@ impl StacksClient { pub fn send_message( &mut self, id: u32, - message: Message, + message: Packet, ) -> Result { let message_bytes = bincode::serialize(&message)?; let slot_id = slot_id(id, &message.msg); @@ -101,18 +101,17 @@ impl StacksClient { } /// Helper function to determine the slot ID for the provided stacker-db writer id and the message type -fn slot_id(id: u32, message: &MessageTypes) -> u32 { +fn slot_id(id: u32, message: &Message) -> u32 { let slot_id = match message { - MessageTypes::DkgBegin(_) => 0, - MessageTypes::DkgPrivateBegin(_) => 1, - MessageTypes::DkgEnd(_) => 2, - MessageTypes::DkgPublicEnd(_) => 3, - MessageTypes::DkgPublicShare(_) => 4, - MessageTypes::DkgPrivateShares(_) => 5, - MessageTypes::NonceRequest(_) => 6, - MessageTypes::NonceResponse(_) => 7, - MessageTypes::SignShareRequest(_) => 8, - MessageTypes::SignShareResponse(_) => 9, + Message::DkgBegin(_) => 0, + Message::DkgPrivateBegin(_) => 1, + Message::DkgEnd(_) => 2, + Message::DkgPublicShares(_) => 4, + Message::DkgPrivateShares(_) => 5, + Message::NonceRequest(_) => 6, + Message::NonceResponse(_) => 7, + Message::SignatureShareRequest(_) => 8, + Message::SignatureShareResponse(_) => 9, }; SLOTS_PER_USER * id + slot_id } From 3fde24d477e18a23e0f941b2bf07544cef70cd68 Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Thu, 28 Sep 2023 17:17:55 -0400 Subject: [PATCH 2/6] remove unnecessary crypto module since all that code has been moved into wsts --- stacks-signer/src/crypto/frost.rs | 881 ------------------------------ stacks-signer/src/crypto/mod.rs | 2 - 2 files changed, 883 deletions(-) delete mode 100644 stacks-signer/src/crypto/frost.rs delete mode 100644 stacks-signer/src/crypto/mod.rs diff --git a/stacks-signer/src/crypto/frost.rs b/stacks-signer/src/crypto/frost.rs deleted file mode 100644 index f1d3220ac..000000000 --- a/stacks-signer/src/crypto/frost.rs +++ /dev/null @@ -1,881 +0,0 @@ -use hashbrown::HashSet; -use slog::{slog_info, slog_warn}; -use stacks_common::{error, info, warn}; -use std::collections::BTreeMap; -use wsts::{ - common::{PolyCommitment, PublicNonce, Signature, SignatureShare}, - compute, - errors::AggregatorError, - net::{ - DkgBegin, DkgPublicShares, NonceRequest, NonceResponse, Signable, SignatureShareRequest, Message, Packet, - }, - state_machine::{ - coordinator::{ - Coordinatable, Coordinator, - } - OperationResult, - }, - taproot::SchnorrProof, - v1, Point, Scalar, -}; - -#[derive(thiserror::Error, Debug)] -/// The error type for the coordinator -pub enum Error { - /// A bad state change was made - #[error("Bad State Change: {0}")] - BadStateChange(String), - /// A bad dkg_id in received message - #[error("Bad dkg_id: got {0} expected {1}")] - BadDkgId(u64, u64), - /// A bad dkg_public_id in received message - #[error("Bad dkg_public_id: got {0} expected {1}")] - BadDkgPublicId(u64, u64), - /// A bad sign_id in received message - #[error("Bad sign_id: got {0} expected {1}")] - BadSignId(u64, u64), - /// A bad sign_nonce_id in received message - #[error("Bad sign_nonce_id: got {0} expected {1}")] - BadSignNonceId(u64, u64), - /// SignatureAggregator error - #[error("Aggregator: {0}")] - Aggregator(AggregatorError), - /// Schnorr proof failed to verify - #[error("Schnorr Proof failed to verify")] - SchnorrProofFailed, -} - -impl From for Error { - fn from(err: AggregatorError) -> Self { - Error::Aggregator(err) - } -} - - -/// The coordinator for the FROST algorithm -pub struct Coordinator { - current_dkg_id: u64, - current_dkg_public_id: u64, - current_sign_id: u64, - current_sign_nonce_id: u64, - total_signers: u32, // Assuming the signers cover all id:s in {1, 2, ..., total_signers} - total_keys: u32, - threshold: u32, - dkg_public_shares: BTreeMap, - public_nonces: BTreeMap, - signature_shares: BTreeMap>, - aggregate_public_key: Point, - signature: Signature, - schnorr_proof: SchnorrProof, - message_private_key: Scalar, - ids_to_await: HashSet, - message: Vec, - state: State, -} - -impl Coordinator { - /// Create a new coordinator - pub fn new( - total_signers: u32, - total_keys: u32, - threshold: u32, - message_private_key: Scalar, - ) -> Self { - Self { - current_dkg_id: 0, - current_dkg_public_id: 0, - current_sign_id: 0, - current_sign_nonce_id: 0, - total_signers, - total_keys, - threshold, - dkg_public_shares: Default::default(), - public_nonces: Default::default(), - signature_shares: Default::default(), - aggregate_public_key: Point::default(), - signature: Signature { - R: Default::default(), - z: Default::default(), - }, - schnorr_proof: SchnorrProof { - r: Default::default(), - s: Default::default(), - }, - message: Default::default(), - message_private_key, - ids_to_await: (0..total_signers).collect(), - state: State::Idle, - } - } -} - -impl Coordinator { - /// Handle a message from the stacker-db instance - pub fn process_message( - &mut self, - message: &Packet, - ) -> Result<(Option, Option), Error> { - loop { - match self.state { - State::Idle => { - // do nothing - // We are the coordinator and should be the only thing triggering messages right now - return Ok((None, None)); - } - State::DkgPublicDistribute => { - let message = self.start_public_shares()?; - return Ok((Some(message), None)); - } - State::DkgPublicGather => { - self.gather_public_shares(message)?; - if self.state == State::DkgPublicGather { - // We need more data - return Ok((None, None)); - } - } - State::DkgPrivateDistribute => { - let message = self.start_private_shares()?; - return Ok((Some(message), None)); - } - State::DkgEndGather => { - self.gather_dkg_end(message)?; - if self.state == State::DkgEndGather { - // We need more data - return Ok((None, None)); - } else if self.state == State::Idle { - // We are done with the DKG round! Return the operation result - return Ok((None, Some(OperationResult::Dkg(self.aggregate_public_key)))); - } - } - State::NonceRequest => { - let message = self.request_nonces()?; - return Ok((Some(message), None)); - } - State::NonceGather => { - self.gather_nonces(message)?; - if self.state == State::NonceGather { - // We need more data - return Ok((None, None)); - } - } - State::SigShareRequest => { - let message = self.request_sig_shares()?; - return Ok((Some(message), None)); - } - State::SigShareGather => { - self.gather_sig_shares(message)?; - if self.state == State::SigShareGather { - // We need more data - return Ok((None, None)); - } else if self.state == State::Idle { - // We are done with the DKG round! Return the operation result - return Ok(( - None, - Some(OperationResult::Sign( - Signature { - R: self.signature.R, - z: self.signature.z, - }, - SchnorrProof { - r: self.schnorr_proof.r, - s: self.schnorr_proof.s, - }, - )), - )); - } - } - } - } - } - - /// Start a DKG round - pub fn start_dkg_round(&mut self) -> Result { - self.current_dkg_id = self.current_dkg_id.wrapping_add(1); - info!("Starting DKG round #{}", self.current_dkg_id); - self.move_to(State::DkgPublicDistribute)?; - self.start_public_shares() - } - - /// Start a signing round - pub fn start_signing_round(&mut self) -> Result { - self.current_sign_id = self.current_sign_id.wrapping_add(1); - info!("Starting signing round #{}", self.current_sign_id); - self.move_to(State::NonceRequest)?; - self.request_nonces() - } - - fn start_public_shares(&mut self) -> Result { - self.dkg_public_shares.clear(); - info!( - "DKG Round #{}: Starting Public Share Distribution Round #{}", - self.current_dkg_id, self.current_dkg_public_id - ); - let dkg_begin = DkgBegin { - dkg_id: self.current_dkg_id, - }; - - let dkg_begin_message = Packet { - sig: dkg_begin.sign(&self.message_private_key).expect(""), - msg: Message::DkgBegin(dkg_begin), - }; - self.move_to(State::DkgPublicGather)?; - Ok(dkg_begin_message) - } - - fn start_private_shares(&mut self) -> Result { - info!( - "DKG Round #{}: Starting Private Share Distribution", - self.current_dkg_id - ); - let dkg_begin = DkgBegin { - dkg_id: self.current_dkg_id, - }; - let dkg_private_begin_msg = Packet { - sig: dkg_begin.sign(&self.message_private_key).expect(""), - msg: Message::DkgPrivateBegin(dkg_begin), - }; - self.move_to(State::DkgEndGather)?; - Ok(dkg_private_begin_msg) - } - - fn gather_public_shares(&mut self, message: &Packet) -> Result<(), Error> { - match &message.msg { - Message::DkgPublicEnd(dkg_end) => { - if dkg_end.dkg_id != self.current_dkg_id { - return Err(Error::BadDkgId(dkg_end.dkg_id, self.current_dkg_id)); - } - self.ids_to_await.remove(&dkg_end.signer_id); - info!( - "DKG_Public_End round #{} from signer #{}. Waiting on {:?}", - dkg_end.dkg_id, dkg_end.signer_id, self.ids_to_await - ); - } - Message::DkgPublicShare(dkg_public_share) => { - if dkg_public_share.dkg_id != self.current_dkg_id { - return Err(Error::BadDkgId( - dkg_public_share.dkg_id, - self.current_dkg_id, - )); - } - if dkg_public_share.dkg_public_id != self.current_dkg_public_id { - return Err(Error::BadDkgPublicId( - dkg_public_share.dkg_public_id, - self.current_dkg_public_id, - )); - } - self.dkg_public_shares - .insert(dkg_public_share.party_id, dkg_public_share.clone()); - - info!( - "DKG round #{} DKG public round #{} DkgPublicShare from party #{}", - dkg_public_share.dkg_id, - dkg_public_share.dkg_public_id, - dkg_public_share.party_id - ); - } - _ => {} - } - if self.ids_to_await.is_empty() { - // Calculate the aggregate public key - let key = self - .dkg_public_shares - .iter() - .fold(Point::default(), |s, (_, dps)| s + dps.public_share.A[0]); - // check to see if aggregate public key has even y - if key.has_even_y() { - info!("Aggregate public key has even y coord!"); - info!("Aggregate public key: {}", key); - self.aggregate_public_key = key; - self.move_to(State::DkgPrivateDistribute)?; - } else { - warn!("DKG Round #{} Failed: Aggregate public key does not have even y coord, re-running dkg.", self.current_dkg_id); - // TODO: SigningRound seems to break if we inc dkg_public_id - // self.current_dkg_public_id = self.current_dkg_public_id.wrapping_add(1); - self.move_to(State::DkgPublicDistribute)?; - } - self.ids_to_await = (0..self.total_signers).collect(); - } - Ok(()) - } - - fn gather_dkg_end(&mut self, message: &Packet) -> Result<(), Error> { - info!( - "DKG Round #{}: waiting for Dkg End from signers {:?}", - self.current_dkg_id, self.ids_to_await - ); - if let Message::DkgEnd(dkg_end) = &message.msg { - if dkg_end.dkg_id != self.current_dkg_id { - return Err(Error::BadDkgId(dkg_end.dkg_id, self.current_dkg_id)); - } - self.ids_to_await.remove(&dkg_end.signer_id); - info!( - "DKG_End round #{} from signer #{}. Waiting on {:?}", - dkg_end.dkg_id, dkg_end.signer_id, self.ids_to_await - ); - } - - if self.ids_to_await.is_empty() { - self.ids_to_await = (0..self.total_signers).collect(); - self.move_to(State::Idle)?; - } - Ok(()) - } - - fn request_nonces(&mut self) -> Result { - info!( - "Sign Round #{} Nonce round #{} Requesting Nonces", - self.current_sign_id, self.current_sign_nonce_id, - ); - let nonce_request = NonceRequest { - dkg_id: self.current_dkg_id, - sign_id: self.current_sign_id, - sign_nonce_id: self.current_sign_nonce_id, - }; - let nonce_request_msg = Packet { - sig: nonce_request.sign(&self.message_private_key).expect(""), - msg: Message::NonceRequest(nonce_request), - }; - self.ids_to_await = (0..self.total_signers).collect(); - self.move_to(State::NonceGather)?; - Ok(nonce_request_msg) - } - - fn gather_nonces(&mut self, message: &Packet) -> Result<(), Error> { - if let Message::NonceResponse(nonce_response) = &message.msg { - if nonce_response.dkg_id != self.current_dkg_id { - return Err(Error::BadDkgId(nonce_response.dkg_id, self.current_dkg_id)); - } - if nonce_response.sign_id != self.current_sign_id { - return Err(Error::BadSignId( - nonce_response.sign_id, - self.current_sign_id, - )); - } - if nonce_response.sign_nonce_id != self.current_sign_nonce_id { - return Err(Error::BadSignNonceId( - nonce_response.sign_nonce_id, - self.current_sign_nonce_id, - )); - } - - self.public_nonces - .insert(nonce_response.signer_id, nonce_response.clone()); - self.ids_to_await.remove(&nonce_response.signer_id); - info!( - "Sign round #{} nonce round #{} NonceResponse from signer #{}. Waiting on {:?}", - nonce_response.sign_id, - nonce_response.sign_nonce_id, - nonce_response.signer_id, - self.ids_to_await - ); - } - if self.ids_to_await.is_empty() { - // Calculate the aggregate nonce - let aggregate_nonce = self.compute_aggregate_nonce(); - - // check to see if aggregate public key has even y - if aggregate_nonce.has_even_y() { - info!("Aggregate nonce has even y coord!"); - info!("Aggregate nonce: {}", aggregate_nonce); - self.move_to(State::SigShareRequest)?; - } else { - warn!("Sign Round #{} Nonce Round #{} Failed: Aggregate nonce does not have even y coord, requesting new nonces.", self.current_sign_id, self.current_sign_nonce_id); - self.current_sign_nonce_id += 1; - self.move_to(State::NonceRequest)?; - } - } - Ok(()) - } - - fn request_sig_shares(&mut self) -> Result { - info!( - "Sign Round #{} Requesting Signature Shares", - self.current_sign_id, - ); - let nonce_responses = (0..self.total_signers) - .map(|i| self.public_nonces[&i].clone()) - .collect::>(); - let sig_share_request = SignatureShareRequest { - dkg_id: self.current_dkg_id, - sign_id: self.current_sign_id, - correlation_id: 0, // TODO: what is this? - nonce_responses, - message: self.message.clone(), - }; - let sig_share_request_msg = Packet { - sig: sig_share_request.sign(&self.message_private_key).expect(""), - msg: Message::SignShareRequest(sig_share_request), // TODO: either SigShare or SignatureShare, SignShare is right out - }; - self.ids_to_await = (0..self.total_signers).collect(); - self.move_to(State::SigShareGather)?; - Ok(sig_share_request_msg) - } - - fn gather_sig_shares(&mut self, message: &Packet) -> Result<(), Error> { - if let Message::SignShareResponse(sig_share_response) = &message.msg { - if sig_share_response.dkg_id != self.current_dkg_id { - return Err(Error::BadDkgId( - sig_share_response.dkg_id, - self.current_dkg_id, - )); - } - if sig_share_response.sign_id != self.current_sign_id { - return Err(Error::BadSignId( - sig_share_response.sign_id, - self.current_sign_id, - )); - } - self.signature_shares.insert( - sig_share_response.signer_id, - sig_share_response.signature_shares.clone(), - ); - self.ids_to_await.remove(&sig_share_response.signer_id); - info!( - "Sign round #{} SignShareResponse from signer #{}. Waiting on {:?}", - sig_share_response.sign_id, sig_share_response.signer_id, self.ids_to_await - ); - } - if self.ids_to_await.is_empty() { - // Calculate the aggregate signature - let polys: Vec = self - .dkg_public_shares - .values() - .map(|ps| ps.public_share.clone()) - .collect(); - - let nonce_responses = (0..self.total_signers) - .map(|i| self.public_nonces[&i].clone()) - .collect::>(); - - let nonces = nonce_responses - .iter() - .flat_map(|nr| nr.nonces.clone()) - .collect::>(); - - let shares = &self - .public_nonces - .iter() - .flat_map(|(i, _)| self.signature_shares[i].clone()) - .collect::>(); - - info!( - "aggregator.sign({:?}, {:?}, {:?})", - self.message, - nonces.len(), - shares.len() - ); - - let mut aggregator = - v1::Aggregator::new(self.total_keys, self.threshold); - - aggregator.init(polys)?; - - let sig = aggregator.sign(&self.message, &nonces, shares)?; - - info!("Signature ({}, {})", sig.R, sig.z); - - let proof = SchnorrProof::new(&sig)?; - - info!("SchnorrProof ({}, {})", proof.r, proof.s); - - if !proof.verify(&self.aggregate_public_key.x(), &self.message) { - warn!("SchnorrProof failed to verify!"); - return Err(Error::SchnorrProofFailed); - } - - self.move_to(State::Idle)?; - } - Ok(()) - } - - #[allow(non_snake_case)] - fn compute_aggregate_nonce(&self) -> Point { - // XXX this needs to be key_ids for v1 and signer_ids for v2 - let party_ids = self - .public_nonces - .values() - .flat_map(|pn| pn.key_ids.clone()) - .collect::>(); - let nonces = self - .public_nonces - .values() - .flat_map(|pn| pn.nonces.clone()) - .collect::>(); - let (_, R) = compute::intermediate(&self.message, &party_ids, &nonces); - - R - } -} - -#[derive(Debug, PartialEq)] -/// Coordinator states -pub enum State { - /// The coordinator is idle - Idle, - /// The coordinator is distributing public shares - DkgPublicDistribute, - /// The coordinator is gathering public shares - DkgPublicGather, - /// The coordinator is distributing private shares - DkgPrivateDistribute, - /// The coordinator is gathering DKG End messages - DkgEndGather, - /// The coordinator is requesting nonces - NonceRequest, - /// The coordinator is gathering nonces - NonceGather, - /// The coordinator is requesting signature shares - SigShareRequest, - /// The coordinator is gathering signature shares - SigShareGather, -} - -/// The state machine trait for the frost coordinator -pub trait StateMachine { - /// Attempt to move the state machine to a new state - fn move_to(&mut self, state: State) -> Result<(), Error>; - /// Check if the state machine can move to a new state - fn can_move_to(&self, state: &State) -> Result<(), Error>; -} - -impl StateMachine for Coordinator { - fn move_to(&mut self, state: State) -> Result<(), Error> { - self.can_move_to(&state)?; - self.state = state; - Ok(()) - } - - fn can_move_to(&self, state: &State) -> Result<(), Error> { - let prev_state = &self.state; - let accepted = match state { - State::Idle => true, - State::DkgPublicDistribute => { - prev_state == &State::Idle - || prev_state == &State::DkgPublicGather - || prev_state == &State::DkgEndGather - } - State::DkgPublicGather => { - prev_state == &State::DkgPublicDistribute || prev_state == &State::DkgPublicGather - } - State::DkgPrivateDistribute => prev_state == &State::DkgPublicGather, - State::DkgEndGather => prev_state == &State::DkgPrivateDistribute, - State::NonceRequest => { - prev_state == &State::Idle - || prev_state == &State::DkgEndGather - || prev_state == &State::NonceGather - } - State::NonceGather => { - prev_state == &State::NonceRequest || prev_state == &State::NonceGather - } - State::SigShareRequest => prev_state == &State::NonceGather, - State::SigShareGather => { - prev_state == &State::SigShareRequest || prev_state == &State::SigShareGather - } - }; - if accepted { - info!("state change from {:?} to {:?}", prev_state, state); - Ok(()) - } else { - Err(Error::BadStateChange(format!( - "{:?} to {:?}", - prev_state, state - ))) - } - } -} - -impl Coordinatable for Coordinator { - /// Process inbound messages - fn process_inbound_messages( - &mut self, - messages: Vec, - ) -> Result<(Vec, Vec), crate::crypto::Error> { - let mut outbound_messages = vec![]; - let mut operation_results = vec![]; - for message in &messages { - let (outbound_message, operation_result) = self.process_message(message)?; - if let Some(outbound_message) = outbound_message { - outbound_messages.push(outbound_message); - } - if let Some(operation_result) = operation_result { - operation_results.push(operation_result); - } - } - Ok((outbound_messages, operation_results)) - } - - /// Retrieve the aggregate public key - fn get_aggregate_public_key(&self) -> Point { - self.aggregate_public_key - } - - /// Trigger a DKG round - fn start_distributed_key_generation(&mut self) -> Result { - let message = self.start_dkg_round()?; - Ok(message) - } - - // Trigger a signing round - fn start_signing_message(&mut self, message: &[u8]) -> Result { - self.message = message.to_vec(); - let message = self.start_signing_round()?; - Ok(message) - } - - // Reset internal state - fn reset(&mut self) { - self.state = State::Idle; - self.dkg_public_shares.clear(); - self.public_nonces.clear(); - self.signature_shares.clear(); - self.ids_to_await = (0..self.total_signers).collect(); - } -} - -#[cfg(test)] -mod test { - - use crate::runloop::process_inbound_messages; - - use super::*; - use frost_signer::{config::PublicKeys, signing_round::SigningRound}; - use hashbrown::HashMap; - use p256k1::ecdsa; - use rand_core::OsRng; - - #[test] - fn test_state_machine() { - let mut rng = OsRng; - let message_private_key = Scalar::random(&mut rng); - - let mut coordinator = Coordinator::new(3, 3, 3, message_private_key); - assert!(coordinator.can_move_to(&State::DkgPublicDistribute).is_ok()); - assert!(coordinator.can_move_to(&State::DkgPublicGather).is_err()); - assert!(coordinator - .can_move_to(&State::DkgPrivateDistribute) - .is_err()); - assert!(coordinator.can_move_to(&State::DkgEndGather).is_err()); - assert!(coordinator.can_move_to(&State::Idle).is_ok()); - - coordinator.move_to(State::DkgPublicDistribute).unwrap(); - assert!(coordinator - .can_move_to(&State::DkgPublicDistribute) - .is_err()); - assert!(coordinator.can_move_to(&State::DkgPublicGather).is_ok()); - assert!(coordinator - .can_move_to(&State::DkgPrivateDistribute) - .is_err()); - assert!(coordinator.can_move_to(&State::DkgEndGather).is_err()); - assert!(coordinator.can_move_to(&State::Idle).is_ok()); - - coordinator.move_to(State::DkgPublicGather).unwrap(); - assert!(coordinator.can_move_to(&State::DkgPublicDistribute).is_ok()); - assert!(coordinator.can_move_to(&State::DkgPublicGather).is_ok()); - assert!(coordinator - .can_move_to(&State::DkgPrivateDistribute) - .is_ok()); - assert!(coordinator.can_move_to(&State::DkgEndGather).is_err()); - assert!(coordinator.can_move_to(&State::Idle).is_ok()); - - coordinator.move_to(State::DkgPrivateDistribute).unwrap(); - assert!(coordinator - .can_move_to(&State::DkgPublicDistribute) - .is_err()); - assert!(coordinator.can_move_to(&State::DkgPublicGather).is_err()); - assert!(coordinator - .can_move_to(&State::DkgPrivateDistribute) - .is_err()); - assert!(coordinator.can_move_to(&State::DkgEndGather).is_ok()); - assert!(coordinator.can_move_to(&State::Idle).is_ok()); - - coordinator.move_to(State::DkgEndGather).unwrap(); - assert!(coordinator.can_move_to(&State::DkgPublicDistribute).is_ok()); - } - - #[test] - fn test_new_coordinator() { - let total_signers = 10; - let total_keys = 40; - let threshold = 28; - let mut rng = OsRng; - let message_private_key = Scalar::random(&mut rng); - - let coordinator = - Coordinator::new(total_signers, total_keys, threshold, message_private_key); - - assert_eq!(coordinator.total_signers, total_signers); - assert_eq!(coordinator.total_keys, total_keys); - assert_eq!(coordinator.threshold, threshold); - assert_eq!(coordinator.message_private_key, message_private_key); - assert_eq!(coordinator.ids_to_await.len(), total_signers as usize); - assert_eq!(coordinator.state, State::Idle); - } - - #[test] - fn test_start_dkg_round() { - let total_signers = 10; - let total_keys = 40; - let threshold = 28; - let mut rng = OsRng; - let message_private_key = Scalar::random(&mut rng); - let mut coordinator = - Coordinator::new(total_signers, total_keys, threshold, message_private_key); - - let result = coordinator.start_dkg_round(); - - assert!(result.is_ok()); - assert!(matches!(result.unwrap().msg, Message::DkgBegin(_))); - assert_eq!(coordinator.state, State::DkgPublicGather); - assert_eq!(coordinator.current_dkg_id, 1); - } - - #[test] - fn test_start_public_shares() { - let total_signers = 10; - let total_keys = 40; - let threshold = 28; - let mut rng = OsRng; - let message_private_key = Scalar::random(&mut rng); - let mut coordinator = - Coordinator::new(total_signers, total_keys, threshold, message_private_key); - coordinator.state = State::DkgPublicDistribute; // Must be in this state before calling start public shares - - let result = coordinator.start_public_shares().unwrap(); - - assert!(matches!(result.msg, Message::DkgBegin(_))); - assert_eq!(coordinator.state, State::DkgPublicGather); - assert_eq!(coordinator.current_dkg_id, 0); - } - - #[test] - fn test_start_private_shares() { - let total_signers = 10; - let total_keys = 40; - let threshold = 28; - let mut rng = OsRng; - let message_private_key = Scalar::random(&mut rng); - let mut coordinator = - Coordinator::new(total_signers, total_keys, threshold, message_private_key); - coordinator.state = State::DkgPrivateDistribute; // Must be in this state before calling start private shares - - let message = coordinator.start_private_shares().unwrap(); - assert!(matches!(message.msg, Message::DkgPrivateBegin(_))); - assert_eq!(coordinator.state, State::DkgEndGather); - assert_eq!(coordinator.current_dkg_id, 0); - } - - fn setup() -> (Coordinator, Vec) { - let mut rng = OsRng; - let total_signers = 5; - let threshold = total_signers / 10 + 7; - let keys_per_signer = 3; - let total_keys = total_signers * keys_per_signer; - let key_pairs = (0..total_signers) - .map(|_| { - let private_key = Scalar::random(&mut rng); - let public_key = ecdsa::PublicKey::new(&private_key).unwrap(); - (private_key, public_key) - }) - .collect::>(); - let mut key_id: u32 = 0; - let mut signer_ids_map = HashMap::new(); - let mut key_ids_map = HashMap::new(); - let mut key_ids = Vec::new(); - for (i, (_private_key, public_key)) in key_pairs.iter().enumerate() { - for _ in 0..keys_per_signer { - key_ids_map.insert(key_id + 1, *public_key); - key_ids.push(key_id); - key_id += 1; - } - signer_ids_map.insert(i as u32, *public_key); - } - let public_keys = PublicKeys { - signers: signer_ids_map, - key_ids: key_ids_map, - }; - - let signing_rounds = key_pairs - .iter() - .enumerate() - .map(|(signer_id, (private_key, _public_key))| { - SigningRound::new( - threshold, - total_signers, - total_keys, - signer_id as u32, - key_ids.clone(), - *private_key, - public_keys.clone(), - ) - }) - .collect::>(); - - let coordinator = Coordinator::new(total_signers, total_keys, threshold, key_pairs[0].0); - (coordinator, signing_rounds) - } - - /// Helper function for feeding messages back from the processor into the signing rounds and coordinator - fn feedback_messages( - coordinator: &mut Coordinator, - signing_rounds: &mut Vec, - messages: Vec, - ) -> (Vec, Vec) { - let mut inbound_messages = vec![]; - let mut feedback_messages = vec![]; - for signing_round in signing_rounds.as_mut_slice() { - let outbound_messages = - process_inbound_messages(signing_round, messages.clone()).unwrap(); - feedback_messages.extend_from_slice(outbound_messages.as_slice()); - inbound_messages.extend(outbound_messages); - } - for signing_round in signing_rounds.as_mut_slice() { - let outbound_messages = - process_inbound_messages(signing_round, feedback_messages.clone()).unwrap(); - inbound_messages.extend(outbound_messages); - } - coordinator - .process_inbound_messages(inbound_messages) - .unwrap() - } - - #[test] - fn test_process_inbound_messages_dkg() { - let (mut coordinator, mut signing_rounds) = setup(); - // We have started a dkg round - let message = coordinator.start_dkg_round().unwrap(); - assert_eq!(coordinator.aggregate_public_key, Point::default()); - assert_eq!(coordinator.state, State::DkgPublicGather); - // we have to loop in case we get an invalid y coord... - loop { - // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator - let (outbound_messages, operation_results) = - feedback_messages(&mut coordinator, &mut signing_rounds, vec![message.clone()]); - assert!(operation_results.is_empty()); - if coordinator.state == State::DkgEndGather { - // Successfully got an Aggregate Public Key... - assert_eq!(outbound_messages.len(), 1); - match &outbound_messages[0].msg { - Message::DkgPrivateBegin(_) => {} - _ => { - panic!("Expected DkgPrivateBegin message"); - } - } - // Send the DKG Private Begin message to all signers and share their responses with the coordinator and signers - let (outbound_messages, operation_results) = - feedback_messages(&mut coordinator, &mut signing_rounds, outbound_messages); - assert!(outbound_messages.is_empty()); - assert_eq!(operation_results.len(), 1); - match operation_results[0] { - OperationResult::Dkg(point) => { - assert_ne!(point, Point::default()); - assert_eq!(coordinator.aggregate_public_key, point); - assert_eq!(coordinator.state, State::Idle); - break; - } - _ => panic!("Expected Dkg Operation result"), - } - } - } - assert_ne!(coordinator.aggregate_public_key, Point::default()); - } -} diff --git a/stacks-signer/src/crypto/mod.rs b/stacks-signer/src/crypto/mod.rs deleted file mode 100644 index fef2cd9e0..000000000 --- a/stacks-signer/src/crypto/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// FROST (Fast Reliable Optimistic Secure Threshold) signature generation module -pub mod frost; From a131d64b014bee0bf5936c14ad2637c47c9026c6 Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Thu, 28 Sep 2023 17:25:27 -0400 Subject: [PATCH 3/6] fix signer test deps --- Cargo.lock | 1 + testnet/stacks-node/Cargo.toml | 1 + testnet/stacks-node/src/tests/signer.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 479854765..717437e1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3347,6 +3347,7 @@ dependencies = [ "tokio", "toml", "warp", + "wsts", ] [[package]] diff --git a/testnet/stacks-node/Cargo.toml b/testnet/stacks-node/Cargo.toml index 4d63c0cf7..9d461622b 100644 --- a/testnet/stacks-node/Cargo.toml +++ b/testnet/stacks-node/Cargo.toml @@ -39,6 +39,7 @@ stacks-common = { path = "../../stacks-common", features = ["default", "testing" stacks = { package = "stackslib", path = "../../stackslib", features = ["default", "testing"] } stacks-signer = { path = "../../stacks-signer" } p256k1 = "5.4.1" +wsts = { git = "https://github.com/Trust-Machines/wsts", branch = "add-network-types" } [dev-dependencies.rusqlite] version = "=0.24.2" diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index 15ee012f4..25390d0b4 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -21,10 +21,10 @@ use stacks::chainstate::stacks::StacksPrivateKey; use stacks_common::types::chainstate::StacksAddress; use stacks_signer::{ config::Config as SignerConfig, - crypto::{frost::Coordinator as FrostCoordinator, OperationResult}, runloop::RunLoopCommand, utils::{build_signer_config_tomls, build_stackerdb_contract}, }; +use wsts::state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}; // Helper struct for holding the btc and stx neon nodes #[allow(dead_code)] From 57c08bd23861d43905d23297537c9a5fcb71a400 Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Thu, 28 Sep 2023 21:35:17 -0400 Subject: [PATCH 4/6] test signing with and without taproot --- Cargo.lock | 2 +- stacks-signer/src/main.rs | 38 +++++++++++++++++++------ stacks-signer/src/runloop.rs | 16 +++++++++-- testnet/stacks-node/src/tests/signer.rs | 15 ++++++++-- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 717437e1e..75e1588fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4416,7 +4416,7 @@ dependencies = [ [[package]] name = "wsts" version = "4.0.0" -source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#e3d80a9b9d1d7a75ad3b0c95bc75964b00c9de39" +source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#d7829b67907bf914a76a7c3220aa1298e95390b1" dependencies = [ "aes-gcm 0.10.2", "bs58 0.5.0", diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 79f1f3967..fcd7c0132 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -114,10 +114,16 @@ fn process_dkg_result(dkg_res: &[OperationResult]) { OperationResult::Dkg(point) => { println!("Received aggregate group key: {point}"); } - OperationResult::Sign(signature, schnorr_proof) => { + OperationResult::Sign(signature) => { panic!( - "Received unexpected signature ({},{}) and schnorr proof ({},{})", - &signature.R, &signature.z, &schnorr_proof.r, &schnorr_proof.s + "Received unexpected signature ({},{})", + &signature.R, &signature.z, + ); + } + OperationResult::SignTaproot(schnorr_proof) => { + panic!( + "Received unexpected schnorr proof ({},{})", + &schnorr_proof.r, &schnorr_proof.s, ); } } @@ -131,10 +137,16 @@ fn process_sign_result(sign_res: &[OperationResult]) { OperationResult::Dkg(point) => { panic!("Received unexpected aggregate group key: {point}"); } - OperationResult::Sign(signature, schnorr_proof) => { - println!( - "Received good signature ({},{}) and schnorr proof ({},{})", - &signature.R, &signature.z, &schnorr_proof.r, &schnorr_proof.s + OperationResult::Sign(signature) => { + panic!( + "Received bood signature ({},{})", + &signature.R, &signature.z, + ); + } + OperationResult::SignTaproot(schnorr_proof) => { + panic!( + "Received unexpected schnorr proof ({},{})", + &schnorr_proof.r, &schnorr_proof.s, ); } } @@ -184,7 +196,11 @@ fn handle_sign(args: SignArgs) { let spawned_signer = spawn_running_signer(&args.config); spawned_signer .cmd_send - .send(RunLoopCommand::Sign { message: args.data }) + .send(RunLoopCommand::Sign { + message: args.data, + is_taproot: false, + merkle_root: None, + }) .unwrap(); let sign_res = spawned_signer.res_recv.recv().unwrap(); process_sign_result(&sign_res); @@ -198,7 +214,11 @@ fn handle_dkg_sign(args: SignArgs) { spawned_signer.cmd_send.send(RunLoopCommand::Dkg).unwrap(); spawned_signer .cmd_send - .send(RunLoopCommand::Sign { message: args.data }) + .send(RunLoopCommand::Sign { + message: args.data, + is_taproot: false, + merkle_root: None, + }) .unwrap(); let dkg_res = spawned_signer.res_recv.recv().unwrap(); process_dkg_result(&dkg_res); diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 9fbd616ac..4a478bdd8 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -5,6 +5,7 @@ use slog::{slog_debug, slog_error, slog_info, slog_warn}; use stacks_common::{debug, error, info, warn}; use std::{collections::VecDeque, sync::mpsc::Sender, time::Duration}; use wsts::{ + common::MerkleRoot, net::{Message, Packet, Signable}, state_machine::{ coordinator::{Coordinatable, Coordinator as FrostCoordinator}, @@ -22,6 +23,10 @@ pub enum RunLoopCommand { Sign { /// The bytes to sign message: Vec, + /// Whether to make a taproot signature + is_taproot: bool, + /// Taproot merkle root + merkle_root: Option, }, } @@ -78,9 +83,16 @@ impl RunLoop { } } } - RunLoopCommand::Sign { message } => { + RunLoopCommand::Sign { + message, + is_taproot, + merkle_root, + } => { info!("Signing message: {:?}", message); - match self.coordinator.start_signing_message(message) { + match self + .coordinator + .start_signing_message(message, *is_taproot, *merkle_root) + { Ok(msg) => { let ack = self .stacks_client diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index 25390d0b4..a5b3e92aa 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -224,6 +224,15 @@ fn test_stackerdb_dkg() { coordinator_cmd_send .send(RunLoopCommand::Sign { message: vec![1, 2, 3, 4, 5], + is_taproot: false, + merkle_root: None, + }) + .expect("failed to send Sign command"); + coordinator_cmd_send + .send(RunLoopCommand::Sign { + message: vec![1, 2, 3, 4, 5], + is_taproot: true, + merkle_root: None, }) .expect("failed to send Sign command"); @@ -239,10 +248,12 @@ fn test_stackerdb_dkg() { info!("Received aggregate_group_key {point}"); aggregate_group_key = Some(point); } - OperationResult::Sign(sig, proof) => { + OperationResult::Sign(sig) => { info!("Received Signature ({},{})", &sig.R, &sig.z); - info!("Received SchnorrProof ({},{})", &proof.r, &proof.s); frost_signature = Some(sig); + } + OperationResult::SignTaproot(proof) => { + info!("Received SchnorrProof ({},{})", &proof.r, &proof.s); schnorr_proof = Some(proof); } } From 851217d577e12fea16cfde618714f1965ba3b84d Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Fri, 29 Sep 2023 15:48:02 -0400 Subject: [PATCH 5/6] update stacks-signer and tests to use generic wsts state machines --- Cargo.lock | 2 +- stacks-signer/src/main.rs | 9 ++++++--- stacks-signer/src/runloop.rs | 5 +++-- testnet/stacks-node/src/tests/signer.rs | 9 ++++++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75e1588fd..738c620b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4416,7 +4416,7 @@ dependencies = [ [[package]] name = "wsts" version = "4.0.0" -source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#d7829b67907bf914a76a7c3220aa1298e95390b1" +source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#ee9076af85a1a97523db8e99e3b04291230ef218" dependencies = [ "aes-gcm 0.10.2", "bs58 0.5.0", diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index fcd7c0132..8934f3472 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -56,7 +56,10 @@ use std::{ sync::mpsc::{channel, Receiver, Sender}, time::Duration, }; -use wsts::state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}; +use wsts::{ + state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}, + v1, +}; struct SpawnedSigner { running_signer: RunningSigner>, @@ -90,11 +93,11 @@ fn spawn_running_signer(path: &PathBuf) -> SpawnedSigner { let (cmd_send, cmd_recv) = channel(); let (res_send, res_recv) = channel(); let ev = StackerDBEventReceiver::new(vec![config.stackerdb_contract_id.clone()]); - let runloop: RunLoop = RunLoop::from(&config); + let runloop: RunLoop> = RunLoop::from(&config); let mut signer: Signer< RunLoopCommand, Vec, - RunLoop, + RunLoop>, StackerDBEventReceiver, > = Signer::new(runloop, ev, cmd_recv, res_send); let endpoint = config.node_host; diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 4a478bdd8..a2dd35906 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -12,6 +12,7 @@ use wsts::{ signer::SigningRound, OperationResult, PublicKeys, }, + v1, }; /// Which operation to perform @@ -50,7 +51,7 @@ pub struct RunLoop { /// The signing round used to sign messages // TODO: update this to use frost_signer directly instead of the frost signing round // See: https://github.com/stacks-network/stacks-blockchain/issues/3913 - pub signing_round: SigningRound, + pub signing_round: SigningRound, /// The stacks client pub stacks_client: StacksClient, /// Received Commands that need to be processed @@ -175,7 +176,7 @@ impl RunLoop { } } -impl From<&Config> for RunLoop { +impl From<&Config> for RunLoop> { /// Creates new runloop from a config fn from(config: &Config) -> Self { // TODO: this should be a config option diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index a5b3e92aa..c8fb73b20 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -24,7 +24,10 @@ use stacks_signer::{ runloop::RunLoopCommand, utils::{build_signer_config_tomls, build_stackerdb_contract}, }; -use wsts::state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}; +use wsts::{ + state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}, + v1, +}; // Helper struct for holding the btc and stx neon nodes #[allow(dead_code)] @@ -42,12 +45,12 @@ fn spawn_signer( ) -> RunningSigner> { let config = stacks_signer::config::Config::load_from_str(data).unwrap(); let ev = StackerDBEventReceiver::new(vec![config.stackerdb_contract_id.clone()]); - let runloop: stacks_signer::runloop::RunLoop = + let runloop: stacks_signer::runloop::RunLoop> = stacks_signer::runloop::RunLoop::from(&config); let mut signer: Signer< RunLoopCommand, Vec, - stacks_signer::runloop::RunLoop, + stacks_signer::runloop::RunLoop>, StackerDBEventReceiver, > = Signer::new(runloop, ev, receiver, sender); let endpoint = config.endpoint; From 10c82b4ca8a32e2e66dfd01581305d220e0b83e1 Mon Sep 17 00:00:00 2001 From: Joey Yandle Date: Tue, 3 Oct 2023 03:08:11 -0400 Subject: [PATCH 6/6] switch to wsts v2; run signer integration test full scale with 4000 keys --- Cargo.lock | 2 +- stacks-signer/Cargo.toml | 2 +- stacks-signer/src/main.rs | 6 +++--- stacks-signer/src/runloop.rs | 6 +++--- testnet/stacks-node/Cargo.toml | 2 +- testnet/stacks-node/src/tests/signer.rs | 10 +++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 738c620b3..ccbbb440c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4416,7 +4416,7 @@ dependencies = [ [[package]] name = "wsts" version = "4.0.0" -source = "git+https://github.com/Trust-Machines/wsts?branch=add-network-types#ee9076af85a1a97523db8e99e3b04291230ef218" +source = "git+https://github.com/Trust-Machines/wsts?tag=4.0.0rc1#8167a32c123a4769ee2704fd64f7b0d753ca0f56" dependencies = [ "aes-gcm 0.10.2", "bs58 0.5.0", diff --git a/stacks-signer/Cargo.toml b/stacks-signer/Cargo.toml index 50211c3eb..4fad7435a 100644 --- a/stacks-signer/Cargo.toml +++ b/stacks-signer/Cargo.toml @@ -37,7 +37,7 @@ slog-term = "2.6.0" stacks-common = { path = "../stacks-common" } thiserror = "1.0" toml = "0.5.6" -wsts = { git = "https://github.com/Trust-Machines/wsts", branch = "add-network-types" } +wsts = { git = "https://github.com/Trust-Machines/wsts", tag = "4.0.0rc1" } [dependencies.serde_json] version = "1.0" diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 8934f3472..aefc99338 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -58,7 +58,7 @@ use std::{ }; use wsts::{ state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}, - v1, + v2, }; struct SpawnedSigner { @@ -93,11 +93,11 @@ fn spawn_running_signer(path: &PathBuf) -> SpawnedSigner { let (cmd_send, cmd_recv) = channel(); let (res_send, res_recv) = channel(); let ev = StackerDBEventReceiver::new(vec![config.stackerdb_contract_id.clone()]); - let runloop: RunLoop> = RunLoop::from(&config); + let runloop: RunLoop> = RunLoop::from(&config); let mut signer: Signer< RunLoopCommand, Vec, - RunLoop>, + RunLoop>, StackerDBEventReceiver, > = Signer::new(runloop, ev, cmd_recv, res_send); let endpoint = config.node_host; diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index a2dd35906..8035878b2 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -12,7 +12,7 @@ use wsts::{ signer::SigningRound, OperationResult, PublicKeys, }, - v1, + v2, }; /// Which operation to perform @@ -51,7 +51,7 @@ pub struct RunLoop { /// The signing round used to sign messages // TODO: update this to use frost_signer directly instead of the frost signing round // See: https://github.com/stacks-network/stacks-blockchain/issues/3913 - pub signing_round: SigningRound, + pub signing_round: SigningRound, /// The stacks client pub stacks_client: StacksClient, /// Received Commands that need to be processed @@ -176,7 +176,7 @@ impl RunLoop { } } -impl From<&Config> for RunLoop> { +impl From<&Config> for RunLoop> { /// Creates new runloop from a config fn from(config: &Config) -> Self { // TODO: this should be a config option diff --git a/testnet/stacks-node/Cargo.toml b/testnet/stacks-node/Cargo.toml index 9d461622b..bb1934beb 100644 --- a/testnet/stacks-node/Cargo.toml +++ b/testnet/stacks-node/Cargo.toml @@ -39,7 +39,7 @@ stacks-common = { path = "../../stacks-common", features = ["default", "testing" stacks = { package = "stackslib", path = "../../stackslib", features = ["default", "testing"] } stacks-signer = { path = "../../stacks-signer" } p256k1 = "5.4.1" -wsts = { git = "https://github.com/Trust-Machines/wsts", branch = "add-network-types" } +wsts = { git = "https://github.com/Trust-Machines/wsts", tag = "4.0.0rc1" } [dev-dependencies.rusqlite] version = "=0.24.2" diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index c8fb73b20..0bb9112df 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -26,7 +26,7 @@ use stacks_signer::{ }; use wsts::{ state_machine::{coordinator::Coordinator as FrostCoordinator, OperationResult}, - v1, + v2, }; // Helper struct for holding the btc and stx neon nodes @@ -45,12 +45,12 @@ fn spawn_signer( ) -> RunningSigner> { let config = stacks_signer::config::Config::load_from_str(data).unwrap(); let ev = StackerDBEventReceiver::new(vec![config.stackerdb_contract_id.clone()]); - let runloop: stacks_signer::runloop::RunLoop> = + let runloop: stacks_signer::runloop::RunLoop> = stacks_signer::runloop::RunLoop::from(&config); let mut signer: Signer< RunLoopCommand, Vec, - stacks_signer::runloop::RunLoop>, + stacks_signer::runloop::RunLoop>, StackerDBEventReceiver, > = Signer::new(runloop, ev, receiver, sender); let endpoint = config.endpoint; @@ -157,8 +157,8 @@ fn test_stackerdb_dkg() { return; } // Generate Signer Data - let num_signers: u32 = 5; - let num_keys: u32 = 20; + let num_signers: u32 = 16; + let num_keys: u32 = 4000; let signer_stacks_private_keys = (0..num_signers) .map(|_| StacksPrivateKey::new()) .collect::>();