diff --git a/Cargo.lock b/Cargo.lock index 3ee6c8ebc..437f61f6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3147,7 +3147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] @@ -4400,9 +4400,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p256k1" -version = "7.1.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40a031a559eb38c35a14096f21c366254501a06d41c4b327d2a7515d713a5b7" +checksum = "f8641765697df03a529cd21181fbccd1cad9f66086c24252b91c16bf38cdddd6" dependencies = [ "bindgen", "bitvec", @@ -7759,8 +7759,8 @@ dependencies = [ [[package]] name = "wsts" -version = "10.0.0" -source = "git+https://github.com/Trust-Machines/wsts.git?rev=ebd7d7775ad5e44cdbf4f5c1fb468bdf6c467265#ebd7d7775ad5e44cdbf4f5c1fb468bdf6c467265" +version = "11.0.0" +source = "git+https://github.com/Trust-Machines/wsts.git?rev=e78101f923f517440e389f9da7bbf0e89bd901c8#e78101f923f517440e389f9da7bbf0e89bd901c8" dependencies = [ "aes-gcm", "bs58 0.5.1", diff --git a/protobufs/crypto/wsts/wsts.proto b/protobufs/crypto/wsts/wsts.proto index 0f9fcd5eb..c1bb0bfe0 100644 --- a/protobufs/crypto/wsts/wsts.proto +++ b/protobufs/crypto/wsts/wsts.proto @@ -47,6 +47,8 @@ message DkgPrivateBegin { repeated uint32 signer_ids = 2; // Key IDs who responded in time for this DKG round repeated uint32 key_ids = 3; + // Public shares from all signers in this DKG round + DkgPublicShares dkg_public_shares = 4; } // DKG private shares message from signer to all signers and coordinator @@ -89,6 +91,8 @@ message DkgEndBegin { repeated uint32 signer_ids = 2; // Key IDs who responded in time for this DKG round repeated uint32 key_ids = 3; + // Private shares from all signers in this DKG round + map dkg_private_shares = 4; } // DKG end message from signers to coordinator diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 86d482cf8..90948ba47 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -50,7 +50,7 @@ tracing-attributes.workspace = true tracing-subscriber = { workspace = true } url.workspace = true # wsts.workspace = true -wsts = { git = "https://github.com/Trust-Machines/wsts.git", rev = "ebd7d7775ad5e44cdbf4f5c1fb468bdf6c467265" } +wsts = { git = "https://github.com/Trust-Machines/wsts.git", rev = "e78101f923f517440e389f9da7bbf0e89bd901c8" } zeromq.workspace = true hex.workspace = true cfg-if = "1.0" diff --git a/signer/src/main.rs b/signer/src/main.rs index 47d3e28c4..227307181 100644 --- a/signer/src/main.rs +++ b/signer/src/main.rs @@ -320,7 +320,7 @@ async fn run_transaction_signer(ctx: impl Context) -> Result<(), Error> { rng: rand::thread_rng(), signer_private_key: config.signer.private_key, wsts_state_machines: HashMap::new(), - dkg_begin_pause: Some(Duration::from_secs(10)), + dkg_begin_pause: None, //Some(Duration::from_secs(10)), }; signer.run().await diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index aec898972..b474b50bf 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -14,6 +14,7 @@ use bitcoin::OutPoint; use bitvec::array::BitArray; use clarity::codec::StacksMessageCodec as _; use clarity::vm::types::PrincipalData; +use hashbrown::HashMap; use p256k1::point::Point; use p256k1::scalar::Scalar; use polynomial::Polynomial; @@ -649,21 +650,36 @@ impl From for DkgBegin { impl From for proto::DkgPrivateBegin { fn from(value: DkgPrivateBegin) -> Self { + let shares = value + .dkg_public_shares + .iter() + .map(|(i, s)| (*i, proto::SignerDkgPublicShares::from(s.clone()))) + .collect(); + let dkg_public_shares = Some(proto::DkgPublicShares { shares }); proto::DkgPrivateBegin { dkg_id: value.dkg_id, signer_ids: value.signer_ids, key_ids: value.key_ids, + dkg_public_shares, } } } -impl From for DkgPrivateBegin { - fn from(value: proto::DkgPrivateBegin) -> Self { - DkgPrivateBegin { +impl TryFrom for DkgPrivateBegin { + type Error = Error; + fn try_from(value: proto::DkgPrivateBegin) -> Result { + let mut dkg_public_shares = HashMap::new(); + if let Some(shares) = value.dkg_public_shares { + for (id, share) in shares.shares { + dkg_public_shares.insert(id, DkgPublicShares::try_from(share)?); + } + } + Ok(DkgPrivateBegin { dkg_id: value.dkg_id, signer_ids: value.signer_ids, key_ids: value.key_ids, - } + dkg_public_shares, + }) } } @@ -720,17 +736,28 @@ impl From for proto::DkgEndBegin { dkg_id: value.dkg_id, signer_ids: value.signer_ids, key_ids: value.key_ids, + dkg_private_shares: value + .dkg_private_shares + .iter() + .map(|(i, s)| (*i, proto::DkgPrivateShares::from(s.clone()))) + .collect(), } } } -impl From for DkgEndBegin { - fn from(value: proto::DkgEndBegin) -> Self { - DkgEndBegin { +impl TryFrom for DkgEndBegin { + type Error = Error; + fn try_from(value: proto::DkgEndBegin) -> Result { + let mut dkg_private_shares = HashMap::new(); + for (id, shares) in value.dkg_private_shares { + dkg_private_shares.insert(id, DkgPrivateShares::try_from(shares.clone())?); + } + Ok(DkgEndBegin { dkg_id: value.dkg_id, signer_ids: value.signer_ids, key_ids: value.key_ids, - } + dkg_private_shares, + }) } } @@ -1131,13 +1158,13 @@ impl TryFrom for WstsMessage { wsts::net::Message::DkgPublicShares(inner.try_into()?) } proto::wsts_message::Inner::DkgPrivateBegin(inner) => { - wsts::net::Message::DkgPrivateBegin(inner.into()) + wsts::net::Message::DkgPrivateBegin(inner.try_into()?) } proto::wsts_message::Inner::DkgPrivateShares(inner) => { wsts::net::Message::DkgPrivateShares(inner.try_into()?) } proto::wsts_message::Inner::DkgEndBegin(inner) => { - wsts::net::Message::DkgEndBegin(inner.into()) + wsts::net::Message::DkgEndBegin(inner.try_into()?) } proto::wsts_message::Inner::DkgEnd(inner) => { wsts::net::Message::DkgEnd(inner.try_into()?) @@ -1933,11 +1960,12 @@ mod tests { } } impl Dummy for DkgPrivateBegin { - fn dummy_with_rng(_: &Unit, rng: &mut R) -> Self { + fn dummy_with_rng(config: &Unit, rng: &mut R) -> Self { DkgPrivateBegin { dkg_id: Faker.fake_with_rng(rng), signer_ids: Faker.fake_with_rng(rng), key_ids: Faker.fake_with_rng(rng), + dkg_public_shares: config.fake_with_rng(rng), } } } @@ -1962,11 +1990,12 @@ mod tests { } impl Dummy for DkgEndBegin { - fn dummy_with_rng(_: &Unit, rng: &mut R) -> Self { + fn dummy_with_rng(config: &Unit, rng: &mut R) -> Self { DkgEndBegin { dkg_id: Faker.fake_with_rng(rng), signer_ids: Faker.fake_with_rng(rng), key_ids: Faker.fake_with_rng(rng), + dkg_private_shares: config.fake_with_rng(rng), } } } @@ -2217,6 +2246,24 @@ mod tests { } } + impl Dummy for hashbrown::HashMap { + fn dummy_with_rng(config: &Unit, rng: &mut R) -> Self { + fake::vec![(); 0..20] + .into_iter() + .map(|_| (Faker.fake_with_rng(rng), config.fake_with_rng(rng))) + .collect() + } + } + + impl Dummy for hashbrown::HashMap { + fn dummy_with_rng(config: &Unit, rng: &mut R) -> Self { + fake::vec![(); 0..20] + .into_iter() + .map(|_| (Faker.fake_with_rng(rng), config.fake_with_rng(rng))) + .collect() + } + } + #[test] fn conversion_between_bytes_and_uint256() { let number = proto::Uint256 { diff --git a/signer/src/proto/generated/crypto.wsts.rs b/signer/src/proto/generated/crypto.wsts.rs index 804b91422..9f3c18189 100644 --- a/signer/src/proto/generated/crypto.wsts.rs +++ b/signer/src/proto/generated/crypto.wsts.rs @@ -137,6 +137,9 @@ pub struct DkgPrivateBegin { /// Key IDs who responded in time for this DKG round #[prost(uint32, repeated, tag = "3")] pub key_ids: ::prost::alloc::vec::Vec, + /// Public shares from all signers in this DKG round + #[prost(message, optional, tag = "4")] + pub dkg_public_shares: ::core::option::Option, } /// DKG private shares message from signer to all signers and coordinator /// This maps to this type <> @@ -193,6 +196,9 @@ pub struct DkgEndBegin { /// Key IDs who responded in time for this DKG round #[prost(uint32, repeated, tag = "3")] pub key_ids: ::prost::alloc::vec::Vec, + /// Private shares from all signers in this DKG round + #[prost(btree_map = "uint32, message", tag = "4")] + pub dkg_private_shares: ::prost::alloc::collections::BTreeMap, } /// DKG end message from signers to coordinator /// This maps to this type <> diff --git a/signer/src/testing/message.rs b/signer/src/testing/message.rs index c9153bde5..6929c7e31 100644 --- a/signer/src/testing/message.rs +++ b/signer/src/testing/message.rs @@ -132,6 +132,7 @@ impl fake::Dummy for message::WstsMessage { dkg_id: config.fake_with_rng(rng), signer_ids: config.fake_with_rng(rng), key_ids: config.fake_with_rng(rng), + dkg_private_shares: Default::default(), }; Self { diff --git a/signer/src/testing/wsts.rs b/signer/src/testing/wsts.rs index 6dbdcecb9..e65e0bc9f 100644 --- a/signer/src/testing/wsts.rs +++ b/signer/src/testing/wsts.rs @@ -1,5 +1,6 @@ //! Test utilities for running a wsts signer and coordinator. +use rand::rngs::OsRng; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::time::Duration; @@ -122,6 +123,7 @@ impl Coordinator { sign_timeout: None, signer_key_ids, signer_public_keys, + embed_public_private_shares: true, }; let wsts_coordinator = fire::Coordinator::new(config); @@ -313,6 +315,7 @@ impl Signer { /// Participate in a DKG round and return the result pub async fn run_until_dkg_end(mut self) -> Self { let future = async move { + let mut rng = OsRng; loop { let msg = self.network.receive().await.expect("network error"); let bitcoin_chain_tip = msg.bitcoin_chain_tip; @@ -328,12 +331,12 @@ impl Signer { let outbound_packets = self .wsts_signer - .process_inbound_messages(&[packet]) + .process_inbound_messages(&[packet], &mut rng) .expect("message processing failed"); for packet in outbound_packets { self.wsts_signer - .process_inbound_messages(&[packet.clone()]) + .process_inbound_messages(&[packet.clone()], &mut rng) .expect("message processing failed"); self.send_packet(bitcoin_chain_tip, wsts_msg.txid, packet.clone()) @@ -353,6 +356,7 @@ impl Signer { /// Participate in a signing round and return the result pub async fn run_until_signature_share_response(mut self) -> Self { let future = async move { + let mut rng = OsRng; loop { let msg = self.network.receive().await.expect("network error"); let bitcoin_chain_tip = msg.bitcoin_chain_tip; @@ -368,12 +372,12 @@ impl Signer { let outbound_packets = self .wsts_signer - .process_inbound_messages(&[packet]) + .process_inbound_messages(&[packet], &mut rng) .expect("message processing failed"); for packet in outbound_packets { self.wsts_signer - .process_inbound_messages(&[packet.clone()]) + .process_inbound_messages(&[packet.clone()], &mut rng) .expect("message processing failed"); self.send_packet(bitcoin_chain_tip, wsts_msg.txid, packet.clone()) diff --git a/signer/src/transaction_signer.rs b/signer/src/transaction_signer.rs index e5aa95751..9c0163bab 100644 --- a/signer/src/transaction_signer.rs +++ b/signer/src/transaction_signer.rs @@ -39,6 +39,7 @@ use crate::wsts_state_machine::SignerStateMachine; use bitcoin::hashes::Hash; use bitcoin::TapSighash; use futures::StreamExt; +use rand::rngs::OsRng; use wsts::net::DkgEnd; use wsts::net::DkgStatus; use wsts::net::Message as WstsNetMessage; @@ -532,7 +533,7 @@ where &mut self, msg: &message::WstsMessage, bitcoin_chain_tip: &model::BitcoinBlockHash, - msg_public_key: PublicKey, + _msg_public_key: PublicKey, chain_tip_report: &MsgChainTipReport, ) -> Result<(), Error> { match &msg.inner { @@ -574,48 +575,6 @@ where self.relay_message(msg.txid, &msg.inner, bitcoin_chain_tip) .await?; } - WstsNetMessage::DkgPublicShares(dkg_public_shares) => { - tracing::info!( - signer_id = %dkg_public_shares.signer_id, - "handling DkgPublicShares", - ); - let public_keys = match self.wsts_state_machines.get(&msg.txid) { - Some(state_machine) => &state_machine.public_keys, - None => return Err(Error::MissingStateMachine), - }; - let signer_public_key = match public_keys.signers.get(&dkg_public_shares.signer_id) - { - Some(key) => PublicKey::from(key), - None => return Err(Error::MissingPublicKey), - }; - - if signer_public_key != msg_public_key { - return Err(Error::InvalidSignature); - } - self.relay_message(msg.txid, &msg.inner, bitcoin_chain_tip) - .await?; - } - WstsNetMessage::DkgPrivateShares(dkg_private_shares) => { - tracing::info!( - signer_id = %dkg_private_shares.signer_id, - "handling DkgPrivateShares" - ); - let public_keys = match self.wsts_state_machines.get(&msg.txid) { - Some(state_machine) => &state_machine.public_keys, - None => return Err(Error::MissingStateMachine), - }; - let signer_public_key = match public_keys.signers.get(&dkg_private_shares.signer_id) - { - Some(key) => PublicKey::from(key), - None => return Err(Error::MissingPublicKey), - }; - - if signer_public_key != msg_public_key { - return Err(Error::InvalidSignature); - } - self.relay_message(msg.txid, &msg.inner, bitcoin_chain_tip) - .await?; - } WstsNetMessage::DkgEndBegin(_) => { tracing::info!("handling DkgEndBegin"); if !chain_tip_report.sender_is_coordinator { @@ -691,7 +650,11 @@ where } } } - WstsNetMessage::NonceResponse(_) | WstsNetMessage::SignatureShareResponse(_) => { + + WstsNetMessage::DkgPublicShares(_) + | WstsNetMessage::DkgPrivateShares(_) + | WstsNetMessage::NonceResponse(_) + | WstsNetMessage::SignatureShareResponse(_) => { tracing::trace!("ignoring message"); } } @@ -711,12 +674,13 @@ where return Ok(()); }; - let outbound_messages = state_machine.process(msg).map_err(Error::Wsts)?; + let mut rng = OsRng; + let outbound_messages = state_machine.process(msg, &mut rng).map_err(Error::Wsts)?; for outbound_message in outbound_messages.iter() { // The WSTS state machine assume we read our own messages state_machine - .process(outbound_message) + .process(outbound_message, &mut rng) .map_err(Error::Wsts)?; } diff --git a/signer/src/wsts_state_machine.rs b/signer/src/wsts_state_machine.rs index 3f0754956..0b7528cef 100644 --- a/signer/src/wsts_state_machine.rs +++ b/signer/src/wsts_state_machine.rs @@ -12,6 +12,7 @@ use crate::keys::SignerScriptPubKey as _; use crate::storage; use crate::storage::model; +use rand::rngs::OsRng; use wsts::common::PolyCommitment; use wsts::state_machine::coordinator::Coordinator as _; use wsts::state_machine::coordinator::State as WstsState; @@ -65,6 +66,7 @@ impl SignerStateMachine { return Err(error::Error::InvalidConfiguration); }; + let mut rng = OsRng; let state_machine = WstsStateMachine::new( threshold, num_parties, @@ -73,6 +75,8 @@ impl SignerStateMachine { key_ids, signer_private_key.into(), public_keys, + true, + &mut rng, ); Ok(Self(state_machine)) @@ -215,6 +219,7 @@ impl CoordinatorStateMachine { sign_timeout: None, signer_key_ids, signer_public_keys, + embed_public_private_shares: true, }; let wsts_coordinator = WstsCoordinator::new(config); diff --git a/signer/tests/integration/transaction_coordinator.rs b/signer/tests/integration/transaction_coordinator.rs index bdf10d7e7..a19792c74 100644 --- a/signer/tests/integration/transaction_coordinator.rs +++ b/signer/tests/integration/transaction_coordinator.rs @@ -1141,7 +1141,7 @@ async fn run_dkg_from_scratch() { testing::storage::drop_db(db).await; } } - +use tracing_subscriber::{fmt, prelude::*, EnvFilter}; /// Test that three signers can successfully sign and broadcast a bitcoin /// transaction. /// @@ -1174,6 +1174,10 @@ async fn run_dkg_from_scratch() { #[cfg_attr(not(feature = "integration-tests"), ignore)] #[tokio::test] async fn sign_bitcoin_transaction() { + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); let (_, signer_key_pairs): (_, [Keypair; 3]) = testing::wallet::regtest_bootstrap_wallet(); let (rpc, faucet) = regtest::initialize_blockchain();