From 450160c94e3cc63342f89b594433b3ca1511a22b Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 5 Oct 2024 00:34:48 -0400 Subject: [PATCH 01/11] move stuff out of the mod.rs file --- signer/src/proto/convert.rs | 69 +++++++++++++++++++++++++++++++++++++ signer/src/proto/mod.rs | 66 ++--------------------------------- 2 files changed, 71 insertions(+), 64 deletions(-) create mode 100644 signer/src/proto/convert.rs diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs new file mode 100644 index 000000000..6035359e4 --- /dev/null +++ b/signer/src/proto/convert.rs @@ -0,0 +1,69 @@ +//! Conversion functions from protobufs to regular types +//! + +use crate::proto::Uint256; + + +impl From<[u8; 32]> for Uint256 { + fn from(value: [u8; 32]) -> Self { + let mut part0 = [0u8; 8]; + let mut part1 = [0u8; 8]; + let mut part2 = [0u8; 8]; + let mut part3 = [0u8; 8]; + + part0.copy_from_slice(&value[..8]); + part1.copy_from_slice(&value[8..16]); + part2.copy_from_slice(&value[16..24]); + part3.copy_from_slice(&value[24..32]); + + Uint256 { + bits_part0: u64::from_le_bytes(part0), + bits_part1: u64::from_le_bytes(part1), + bits_part2: u64::from_le_bytes(part2), + bits_part3: u64::from_le_bytes(part3), + } + } +} + +impl From for [u8; 32] { + fn from(value: Uint256) -> Self { + let mut bytes = [0u8; 32]; + + bytes[..8].copy_from_slice(&value.bits_part0.to_le_bytes()); + bytes[8..16].copy_from_slice(&value.bits_part1.to_le_bytes()); + bytes[16..24].copy_from_slice(&value.bits_part2.to_le_bytes()); + bytes[24..32].copy_from_slice(&value.bits_part3.to_le_bytes()); + bytes + } +} + +#[cfg(test)] +mod tests { + use super::*; + use fake::Fake; + use fake::Faker; + use rand::rngs::OsRng; + + #[test] + fn conversion_between_bytes_and_uint256() { + let number = Uint256 { + bits_part0: Faker.fake_with_rng(&mut OsRng), + bits_part1: Faker.fake_with_rng(&mut OsRng), + bits_part2: Faker.fake_with_rng(&mut OsRng), + bits_part3: Faker.fake_with_rng(&mut OsRng), + }; + + let bytes = <[u8; 32]>::from(number); + let round_trip_number = Uint256::from(bytes); + assert_eq!(round_trip_number, number); + } + + #[test] + fn conversion_between_uint256_and_bytes() { + let bytes: [u8; 32] = Faker.fake_with_rng(&mut OsRng); + let number = Uint256::from(bytes); + + let rount_trip_bytes = <[u8; 32]>::from(number); + assert_eq!(rount_trip_bytes, bytes); + } +} diff --git a/signer/src/proto/mod.rs b/signer/src/proto/mod.rs index 6b6aefcc0..cb6dbb1d9 100644 --- a/signer/src/proto/mod.rs +++ b/signer/src/proto/mod.rs @@ -1,73 +1,11 @@ #![allow(missing_docs)] mod generated; +pub mod convert; + pub use generated::crypto::wsts::*; pub use generated::crypto::*; pub use generated::stacks::signer::v1::stacks_transaction_sign_request::*; pub use generated::stacks::signer::v1::*; pub use generated::stacks::signer::*; pub use generated::stacks::*; - -impl From<[u8; 32]> for Uint256 { - fn from(value: [u8; 32]) -> Self { - let mut part0 = [0u8; 8]; - let mut part1 = [0u8; 8]; - let mut part2 = [0u8; 8]; - let mut part3 = [0u8; 8]; - - part0.copy_from_slice(&value[..8]); - part1.copy_from_slice(&value[8..16]); - part2.copy_from_slice(&value[16..24]); - part3.copy_from_slice(&value[24..32]); - - Uint256 { - bits_part0: u64::from_le_bytes(part0), - bits_part1: u64::from_le_bytes(part1), - bits_part2: u64::from_le_bytes(part2), - bits_part3: u64::from_le_bytes(part3), - } - } -} - -impl From for [u8; 32] { - fn from(value: Uint256) -> Self { - let mut bytes = [0u8; 32]; - - bytes[..8].copy_from_slice(&value.bits_part0.to_le_bytes()); - bytes[8..16].copy_from_slice(&value.bits_part1.to_le_bytes()); - bytes[16..24].copy_from_slice(&value.bits_part2.to_le_bytes()); - bytes[24..32].copy_from_slice(&value.bits_part3.to_le_bytes()); - bytes - } -} - -#[cfg(test)] -mod tests { - use super::*; - use fake::Fake; - use fake::Faker; - use rand::rngs::OsRng; - - #[test] - fn conversion_between_bytes_and_uint256() { - let number = Uint256 { - bits_part0: Faker.fake_with_rng(&mut OsRng), - bits_part1: Faker.fake_with_rng(&mut OsRng), - bits_part2: Faker.fake_with_rng(&mut OsRng), - bits_part3: Faker.fake_with_rng(&mut OsRng), - }; - - let bytes = <[u8; 32]>::from(number); - let round_trip_number = Uint256::from(bytes); - assert_eq!(round_trip_number, number); - } - - #[test] - fn conversion_between_uint256_and_bytes() { - let bytes: [u8; 32] = Faker.fake_with_rng(&mut OsRng); - let number = Uint256::from(bytes); - - let rount_trip_bytes = <[u8; 32]>::from(number); - assert_eq!(rount_trip_bytes, bytes); - } -} From b05429869a8e9828d25b84f10b3425ccd34255c9 Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 5 Oct 2024 16:25:21 -0400 Subject: [PATCH 02/11] Add conversion impls for crypto module protobufs and some of the id types --- signer/src/keys.rs | 8 +++ signer/src/proto/convert.rs | 120 ++++++++++++++++++++++++++++++++++-- signer/src/proto/mod.rs | 1 + 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/signer/src/keys.rs b/signer/src/keys.rs index fe8089069..30252e66d 100644 --- a/signer/src/keys.rs +++ b/signer/src/keys.rs @@ -28,6 +28,7 @@ //! [^3]: https://github.com/Trust-Machines/p256k1/blob/3ecb941c1af13741d52335ef911693b6d6fda94b/p256k1/src/scalar.rs#L245-L257 //! [^4]: https://github.com/bitcoin-core/secp256k1/blob/3fdf146bad042a17f6b2f490ef8bd9d8e774cdbd/src/scalar.h#L31-L36 +use std::ops::Deref; use std::str::FromStr; use bitcoin::ScriptBuf; @@ -44,6 +45,13 @@ use crate::error::Error; #[serde(transparent)] pub struct PublicKey(secp256k1::PublicKey); +impl Deref for PublicKey { + type Target = secp256k1::PublicKey; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl From<&secp256k1::PublicKey> for PublicKey { fn from(value: &secp256k1::PublicKey) -> Self { Self(*value) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index 6035359e4..da73bd919 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,8 +1,26 @@ //! Conversion functions from protobufs to regular types -//! +//! + +use secp256k1::ecdsa::RecoverableSignature; + +use crate::error::Error; +use crate::keys::PublicKey; +use crate::proto; +use crate::storage::model::{BitcoinTxId, StacksTxId}; use crate::proto::Uint256; +pub trait RequiredField: Sized { + type Inner; + fn required(self) -> Result; +} + +impl RequiredField for Option { + type Inner = T; + fn required(self) -> Result { + self.ok_or(Error::TypeConversion) + } +} impl From<[u8; 32]> for Uint256 { fn from(value: [u8; 32]) -> Self { @@ -37,6 +55,100 @@ impl From for [u8; 32] { } } +impl From for proto::PublicKey { + fn from(value: PublicKey) -> Self { + let (x_only, parity) = value.x_only_public_key(); + proto::PublicKey { + x_only_public_key: Some(Uint256::from(x_only.serialize())), + parity_is_odd: parity == secp256k1::Parity::Odd, + } + } +} + +impl TryFrom for PublicKey { + type Error = Error; + fn try_from(value: proto::PublicKey) -> Result { + let x_only: [u8; 32] = value.x_only_public_key.required()?.into(); + let pk = secp256k1::XOnlyPublicKey::from_slice(&x_only).map_err(Error::InvalidPublicKey)?; + let parity = if value.parity_is_odd { + secp256k1::Parity::Odd + } else { + secp256k1::Parity::Even + }; + let public_key = secp256k1::PublicKey::from_x_only_public_key(pk, parity); + Ok(Self::from(public_key)) + } +} + +impl From for proto::RecoverableSignature { + fn from(value: RecoverableSignature) -> Self { + let mut lower_bits = [0; 32]; + let mut upper_bits = [0; 32]; + + let (recovery_id, bytes) = value.serialize_compact(); + + lower_bits.copy_from_slice(&bytes[..32]); + upper_bits.copy_from_slice(&bytes[32..]); + + Self { + lower_bits: Some(Uint256::from(lower_bits)), + upper_bits: Some(Uint256::from(upper_bits)), + recovery_id: recovery_id.to_i32(), + } + } +} + +impl TryFrom for RecoverableSignature { + type Error = Error; + fn try_from(value: proto::RecoverableSignature) -> Result { + let mut data = [0; 64]; + + let lower_bits: [u8; 32] = value.lower_bits.required()?.into(); + let upper_bits: [u8; 32] = value.upper_bits.required()?.into(); + + data[..32].copy_from_slice(&lower_bits); + data[32..].copy_from_slice(&upper_bits); + + let recovery_id = secp256k1::ecdsa::RecoveryId::from_i32(value.recovery_id) + .map_err(Error::InvalidPublicKey)?; + + RecoverableSignature::from_compact(&data, recovery_id) + .map_err(Error::InvalidRecoverableSignatureBytes) + } +} + +impl From for proto::StacksTxid { + fn from(value: StacksTxId) -> Self { + proto::StacksTxid { + txid: Some(Uint256::from(value.into_bytes())), + } + } +} + +impl TryFrom for StacksTxId { + type Error = Error; + fn try_from(value: proto::StacksTxid) -> Result { + let bytes: [u8; 32] = value.txid.required()?.into(); + Ok(StacksTxId::from(bytes)) + } +} + +impl From for proto::BitcoinTxid { + fn from(value: BitcoinTxId) -> Self { + proto::BitcoinTxid { + txid: Some(Uint256::from(value.into_bytes())), + } + } +} + +impl TryFrom for BitcoinTxId { + type Error = Error; + fn try_from(value: proto::BitcoinTxid) -> Result { + let bytes: [u8; 32] = value.txid.required()?.into(); + Ok(BitcoinTxId::from(bytes)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -46,7 +158,7 @@ mod tests { #[test] fn conversion_between_bytes_and_uint256() { - let number = Uint256 { + let number = proto::Uint256 { bits_part0: Faker.fake_with_rng(&mut OsRng), bits_part1: Faker.fake_with_rng(&mut OsRng), bits_part2: Faker.fake_with_rng(&mut OsRng), @@ -54,14 +166,14 @@ mod tests { }; let bytes = <[u8; 32]>::from(number); - let round_trip_number = Uint256::from(bytes); + let round_trip_number = proto::Uint256::from(bytes); assert_eq!(round_trip_number, number); } #[test] fn conversion_between_uint256_and_bytes() { let bytes: [u8; 32] = Faker.fake_with_rng(&mut OsRng); - let number = Uint256::from(bytes); + let number = proto::Uint256::from(bytes); let rount_trip_bytes = <[u8; 32]>::from(number); assert_eq!(rount_trip_bytes, bytes); diff --git a/signer/src/proto/mod.rs b/signer/src/proto/mod.rs index cb6dbb1d9..288525cb3 100644 --- a/signer/src/proto/mod.rs +++ b/signer/src/proto/mod.rs @@ -4,6 +4,7 @@ mod generated; pub mod convert; pub use generated::crypto::wsts::*; +pub use generated::bitcoin::*; pub use generated::crypto::*; pub use generated::stacks::signer::v1::stacks_transaction_sign_request::*; pub use generated::stacks::signer::v1::*; From d95679ddcd0b6f7fc640ac3fe174c52b6b497cd8 Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 5 Oct 2024 21:56:12 -0400 Subject: [PATCH 03/11] Finish the bitcoin types --- signer/src/proto/convert.rs | 53 +++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index da73bd919..7bf23dae0 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,16 +1,20 @@ //! Conversion functions from protobufs to regular types //! +use bitcoin::OutPoint; +use bitcoin::hashes::Hash as _; use secp256k1::ecdsa::RecoverableSignature; use crate::error::Error; use crate::keys::PublicKey; use crate::proto; -use crate::storage::model::{BitcoinTxId, StacksTxId}; +use crate::storage::model::{BitcoinBlockHash, BitcoinTxId, StacksTxId}; use crate::proto::Uint256; -pub trait RequiredField: Sized { +/// This trait is to make it easy to handle fields of protobuf structs that +/// are `None`, when they should be `Some(_)`. +trait RequiredField: Sized { type Inner; fn required(self) -> Result; } @@ -149,6 +153,51 @@ impl TryFrom for BitcoinTxId { } } +impl From for proto::BitcoinBlockHash { + fn from(value: BitcoinBlockHash) -> Self { + proto::BitcoinBlockHash { + block_hash: Some(Uint256::from(value.into_bytes())), + } + } +} + +impl TryFrom for BitcoinBlockHash { + type Error = Error; + fn try_from(value: proto::BitcoinBlockHash) -> Result { + let bytes: [u8; 32] = value.block_hash.required()?.into(); + Ok(BitcoinBlockHash::from(bytes)) + } +} + +impl From for proto::BitcoinTxid { + fn from(value: bitcoin::Txid) -> Self { + proto::BitcoinTxid { + txid: Some(Uint256::from(value.to_byte_array())), + } + } +} + +impl From for proto::OutPoint { + fn from(value: OutPoint) -> Self { + proto::OutPoint { + txid: Some(proto::BitcoinTxid::from(value.txid)), + vout: value.vout, + } + } +} + +impl TryFrom for OutPoint { + type Error = Error; + fn try_from(value: proto::OutPoint) -> Result { + let txid: BitcoinTxId = value.txid.required()?.try_into()?; + + Ok(OutPoint { + txid: txid.into(), + vout: value.vout, + }) + } +} + #[cfg(test)] mod tests { use super::*; From bb74ea9210b34c947beec6106524fe2fa8234abf Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 5 Oct 2024 23:05:01 -0400 Subject: [PATCH 04/11] Finish the conversion for common stacks types --- signer/src/proto/convert.rs | 90 +++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index 7bf23dae0..5e82221c8 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,14 +1,19 @@ //! Conversion functions from protobufs to regular types //! -use bitcoin::OutPoint; use bitcoin::hashes::Hash as _; +use bitcoin::OutPoint; +use clarity::codec::StacksMessageCodec; +use clarity::vm::types::PrincipalData; use secp256k1::ecdsa::RecoverableSignature; +use stacks_common::types::chainstate::StacksAddress; use crate::error::Error; use crate::keys::PublicKey; use crate::proto; -use crate::storage::model::{BitcoinBlockHash, BitcoinTxId, StacksTxId}; +use crate::storage::model::{ + BitcoinBlockHash, BitcoinTxId, StacksBlockHash, StacksPrincipal, StacksTxId, +}; use crate::proto::Uint256; @@ -121,22 +126,6 @@ impl TryFrom for RecoverableSignature { } } -impl From for proto::StacksTxid { - fn from(value: StacksTxId) -> Self { - proto::StacksTxid { - txid: Some(Uint256::from(value.into_bytes())), - } - } -} - -impl TryFrom for StacksTxId { - type Error = Error; - fn try_from(value: proto::StacksTxid) -> Result { - let bytes: [u8; 32] = value.txid.required()?.into(); - Ok(StacksTxId::from(bytes)) - } -} - impl From for proto::BitcoinTxid { fn from(value: BitcoinTxId) -> Self { proto::BitcoinTxid { @@ -198,6 +187,71 @@ impl TryFrom for OutPoint { } } +impl From for proto::StacksTxid { + fn from(value: StacksTxId) -> Self { + proto::StacksTxid { + txid: Some(Uint256::from(value.into_bytes())), + } + } +} + +impl TryFrom for StacksTxId { + type Error = Error; + fn try_from(value: proto::StacksTxid) -> Result { + let bytes: [u8; 32] = value.txid.required()?.into(); + Ok(StacksTxId::from(bytes)) + } +} + +impl From for proto::StacksBlockId { + fn from(value: StacksBlockHash) -> Self { + proto::StacksBlockId { + block_id: Some(Uint256::from(value.into_bytes())), + } + } +} + +impl TryFrom for StacksBlockHash { + type Error = Error; + fn try_from(value: proto::StacksBlockId) -> Result { + let bytes: [u8; 32] = value.block_id.required()?.into(); + Ok(StacksBlockHash::from(bytes)) + } +} + +impl From for proto::StacksAddress { + fn from(value: StacksAddress) -> Self { + proto::StacksAddress { + address: value.serialize_to_vec(), + } + } +} + +impl TryFrom for StacksAddress { + type Error = Error; + fn try_from(value: proto::StacksAddress) -> Result { + let fd = &mut value.address.as_slice(); + StacksAddress::consensus_deserialize(fd).map_err(Error::StacksCodec) + } +} + +impl From for proto::StacksPrincipal { + fn from(value: StacksPrincipal) -> Self { + proto::StacksPrincipal { data: value.serialize_to_vec() } + } +} + +impl TryFrom for StacksPrincipal { + type Error = Error; + fn try_from(value: proto::StacksPrincipal) -> Result { + let fd = &mut value.data.as_slice(); + + PrincipalData::consensus_deserialize(fd) + .map(StacksPrincipal::from) + .map_err(Error::StacksCodec) + } +} + #[cfg(test)] mod tests { use super::*; From cda8a1afd04df84472e44a275a25cc6f5b84ef32 Mon Sep 17 00:00:00 2001 From: djordon Date: Sun, 6 Oct 2024 00:49:31 -0400 Subject: [PATCH 05/11] Add tests for conversions --- signer/src/proto/convert.rs | 107 +++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index 5e82221c8..87be0b80b 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,9 +1,7 @@ //! Conversion functions from protobufs to regular types //! -use bitcoin::hashes::Hash as _; -use bitcoin::OutPoint; -use clarity::codec::StacksMessageCodec; +use clarity::codec::StacksMessageCodec as _; use clarity::vm::types::PrincipalData; use secp256k1::ecdsa::RecoverableSignature; use stacks_common::types::chainstate::StacksAddress; @@ -11,9 +9,11 @@ use stacks_common::types::chainstate::StacksAddress; use crate::error::Error; use crate::keys::PublicKey; use crate::proto; -use crate::storage::model::{ - BitcoinBlockHash, BitcoinTxId, StacksBlockHash, StacksPrincipal, StacksTxId, -}; +use crate::storage::model::BitcoinBlockHash; +use crate::storage::model::BitcoinTxId; +use crate::storage::model::StacksBlockHash; +use crate::storage::model::StacksPrincipal; +use crate::storage::model::StacksTxId; use crate::proto::Uint256; @@ -158,29 +158,21 @@ impl TryFrom for BitcoinBlockHash { } } -impl From for proto::BitcoinTxid { - fn from(value: bitcoin::Txid) -> Self { - proto::BitcoinTxid { - txid: Some(Uint256::from(value.to_byte_array())), - } - } -} - -impl From for proto::OutPoint { - fn from(value: OutPoint) -> Self { +impl From for proto::OutPoint { + fn from(value: bitcoin::OutPoint) -> Self { proto::OutPoint { - txid: Some(proto::BitcoinTxid::from(value.txid)), + txid: Some(proto::BitcoinTxid::from(BitcoinTxId::from(value.txid))), vout: value.vout, } } } -impl TryFrom for OutPoint { +impl TryFrom for bitcoin::OutPoint { type Error = Error; fn try_from(value: proto::OutPoint) -> Result { let txid: BitcoinTxId = value.txid.required()?.try_into()?; - Ok(OutPoint { + Ok(bitcoin::OutPoint { txid: txid.into(), vout: value.vout, }) @@ -254,10 +246,38 @@ impl TryFrom for StacksPrincipal { #[cfg(test)] mod tests { + use crate::keys::PrivateKey; + use super::*; + + use std::marker::PhantomData; + + use bitcoin::hashes::Hash as _; + + use fake::Dummy; use fake::Fake; use fake::Faker; use rand::rngs::OsRng; + use test_case::test_case; + + struct Unit; + + impl Dummy for bitcoin::OutPoint { + fn dummy_with_rng(_: &Unit, rng: &mut R) -> Self { + let bytes: [u8; 32] = Faker.fake_with_rng(rng); + let txid = bitcoin::Txid::from_byte_array(bytes); + let vout: u32 = Faker.fake_with_rng(rng); + bitcoin::OutPoint { txid, vout } + } + } + + impl Dummy for RecoverableSignature { + fn dummy_with_rng(_: &Unit, rng: &mut R) -> Self { + let private_key = PrivateKey::new(rng); + let msg = secp256k1::Message::from_digest([0; 32]); + private_key.sign_ecdsa_recoverable(&msg) + } + } #[test] fn conversion_between_bytes_and_uint256() { @@ -281,4 +301,53 @@ mod tests { let rount_trip_bytes = <[u8; 32]>::from(number); assert_eq!(rount_trip_bytes, bytes); } + + #[test_case(PhantomData::<[u8; 32]>, PhantomData::; "Uint256")] + #[test_case(PhantomData::, PhantomData::; "PublicKey")] + #[test_case(PhantomData::, PhantomData::; "BitcoinTxId")] + #[test_case(PhantomData::, PhantomData::; "BitcoinBlockHash")] + #[test_case(PhantomData::, PhantomData::; "StacksTxId")] + #[test_case(PhantomData::, PhantomData::; "StacksBlockHash")] + #[test_case(PhantomData::, PhantomData::; "StacksPrincipal")] + fn convert_protobuf_types(_: PhantomData, _: PhantomData) + where + // `.unwrap()` requires that `E` implement `std::fmt::Debug` and + // `assert_eq!` requires `PartialEq + std::fmt::Debug`. + T: Dummy + TryFrom + Clone + PartialEq + std::fmt::Debug, + U: From, + E: std::fmt::Debug, + { + // The type T originates from a signer. Let's create a random + // instance of one. + let original: T = Faker.fake_with_rng(&mut OsRng); + // The type U is a protobuf type. Before sending it to other + // signers, we convert our internal type into it's protobuf + // counterpart. We can always infallibly create U from T. + let proto_original = U::from(original.clone()); + + // Some other signer recieves an instance of U. This could be a + // mallicious actor or a modified version of the signer binary + // where they made some mistake, so converting back to T can fail. + let original_from_proto = T::try_from(proto_original).unwrap(); + // In this case, we know U was created from T correctly, so we + // should be able to convert back without issues. + assert_eq!(original, original_from_proto); + } + + /// This test is identical to [`convert_protobuf_types`] tests above, + /// except we cannot implement Dummy on these types. + #[test_case(PhantomData::, PhantomData::; "OutPoint")] + #[test_case(PhantomData::, PhantomData::; "RecoverableSignature")] + fn convert_protobuf_types2(_: PhantomData, _: PhantomData) + where + T: Dummy + TryFrom + Clone + PartialEq + std::fmt::Debug, + U: From, + E: std::fmt::Debug, + { + let original: T = Unit.fake_with_rng(&mut OsRng); + let proto_original = U::from(original.clone()); + + let original_from_proto = T::try_from(proto_original).unwrap(); + assert_eq!(original, original_from_proto); + } } From 61c093a53d274d746bdb2f01d7e31797780be410 Mon Sep 17 00:00:00 2001 From: djordon Date: Sun, 6 Oct 2024 01:29:53 -0400 Subject: [PATCH 06/11] Finish off signer decision types --- protobufs/stacks/signer/v1/decisions.proto | 8 +- signer/src/proto/convert.rs | 96 ++++++++++++++++++- .../src/proto/generated/stacks.signer.v1.rs | 9 +- 3 files changed, 100 insertions(+), 13 deletions(-) diff --git a/protobufs/stacks/signer/v1/decisions.proto b/protobufs/stacks/signer/v1/decisions.proto index 996081e80..b9d77856c 100644 --- a/protobufs/stacks/signer/v1/decisions.proto +++ b/protobufs/stacks/signer/v1/decisions.proto @@ -10,9 +10,7 @@ import "stacks/common.proto"; message SignerDepositDecision { // The bitcoin transaction ID of the transaction containing the deposit // request. It must be 32 bytes. - bitcoin.BitcoinTxid txid = 1; - // Index of the deposit request UTXO. - uint32 output_index = 2; + bitcoin.OutPoint outpoint = 1; // Whether or not the signer has accepted the deposit request. bool accepted = 3; // This specifies whether the sending signer can provide signature shares @@ -20,8 +18,8 @@ message SignerDepositDecision { bool can_sign = 4; } -// Represents a decision to accept or reject a deposit request. -message SignerWithdrawDecision { +// Represents a decision to accept or reject a withdrawal request. +message SignerWithdrawalDecision { // ID of the withdraw request. uint64 request_id = 1; // The Stacks block ID of the Stacks block containing the request. It diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index 87be0b80b..fb9fd4658 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -8,6 +8,10 @@ use stacks_common::types::chainstate::StacksAddress; use crate::error::Error; use crate::keys::PublicKey; +use crate::message::BitcoinTransactionSignAck; +use crate::message::SignerDepositDecision; +use crate::message::SignerWithdrawalDecision; +use crate::message::StacksTransactionSignature; use crate::proto; use crate::storage::model::BitcoinBlockHash; use crate::storage::model::BitcoinTxId; @@ -244,6 +248,90 @@ impl TryFrom for StacksPrincipal { } } +impl From for proto::SignerDepositDecision { + fn from(value: SignerDepositDecision) -> Self { + proto::SignerDepositDecision { + outpoint: Some(proto::OutPoint { + txid: Some(BitcoinTxId::from(value.txid).into()), + vout: value.output_index, + }), + accepted: value.accepted, + } + } +} + +impl TryFrom for SignerDepositDecision { + type Error = Error; + fn try_from(value: proto::SignerDepositDecision) -> Result { + let outpoint: bitcoin::OutPoint = value.outpoint.required()?.try_into()?; + Ok(SignerDepositDecision { + txid: outpoint.txid, + output_index: outpoint.vout, + accepted: value.accepted, + }) + } +} + +impl From for proto::SignerWithdrawalDecision { + fn from(value: SignerWithdrawalDecision) -> Self { + proto::SignerWithdrawalDecision { + request_id: value.request_id, + block_id: Some(StacksBlockHash::from(value.block_hash).into()), + accepted: value.accepted, + txid: Some(value.txid.into()), + } + } +} + +impl TryFrom for SignerWithdrawalDecision { + type Error = Error; + fn try_from(value: proto::SignerWithdrawalDecision) -> Result { + Ok(SignerWithdrawalDecision { + request_id: value.request_id, + block_hash: StacksBlockHash::try_from(value.block_id.required()?)?.into_bytes(), + accepted: value.accepted, + txid: value.txid.required()?.try_into()?, + }) + } +} + +impl From for proto::BitcoinTransactionSignAck { + fn from(value: BitcoinTransactionSignAck) -> Self { + proto::BitcoinTransactionSignAck { + txid: Some(BitcoinTxId::from(value.txid).into()), + } + } +} + +impl TryFrom for BitcoinTransactionSignAck { + type Error = Error; + fn try_from(value: proto::BitcoinTransactionSignAck) -> Result { + Ok(BitcoinTransactionSignAck { + txid: BitcoinTxId::try_from(value.txid.required()?)?.into(), + }) + } +} + +impl From for proto::StacksTransactionSignature { + fn from(value: StacksTransactionSignature) -> Self { + proto::StacksTransactionSignature { + txid: Some(StacksTxId::from(value.txid).into()), + signature: Some(value.signature.into()), + } + } +} + +impl TryFrom for StacksTransactionSignature { + type Error = Error; + fn try_from(value: proto::StacksTransactionSignature) -> Result { + Ok(StacksTransactionSignature { + txid: StacksTxId::try_from(value.txid.required()?)?.into(), + signature: value.signature.required()?.try_into()?, + }) + } +} + + #[cfg(test)] mod tests { use crate::keys::PrivateKey; @@ -309,7 +397,11 @@ mod tests { #[test_case(PhantomData::, PhantomData::; "StacksTxId")] #[test_case(PhantomData::, PhantomData::; "StacksBlockHash")] #[test_case(PhantomData::, PhantomData::; "StacksPrincipal")] - fn convert_protobuf_types(_: PhantomData, _: PhantomData) + #[test_case(PhantomData::, PhantomData::; "SignerDepositDecision")] + #[test_case(PhantomData::, PhantomData::; "SignerWithdrawalDecision")] + #[test_case(PhantomData::, PhantomData::; "BitcoinTransactionSignAck")] + #[test_case(PhantomData::, PhantomData::; "StacksTransactionSignature")] + fn convert_protobuf_type(_: PhantomData, _: PhantomData) where // `.unwrap()` requires that `E` implement `std::fmt::Debug` and // `assert_eq!` requires `PartialEq + std::fmt::Debug`. @@ -338,7 +430,7 @@ mod tests { /// except we cannot implement Dummy on these types. #[test_case(PhantomData::, PhantomData::; "OutPoint")] #[test_case(PhantomData::, PhantomData::; "RecoverableSignature")] - fn convert_protobuf_types2(_: PhantomData, _: PhantomData) + fn convert_protobuf_type2(_: PhantomData, _: PhantomData) where T: Dummy + TryFrom + Clone + PartialEq + std::fmt::Debug, U: From, diff --git a/signer/src/proto/generated/stacks.signer.v1.rs b/signer/src/proto/generated/stacks.signer.v1.rs index 21110bff8..ece0677b6 100644 --- a/signer/src/proto/generated/stacks.signer.v1.rs +++ b/signer/src/proto/generated/stacks.signer.v1.rs @@ -6,10 +6,7 @@ pub struct SignerDepositDecision { /// The bitcoin transaction ID of the transaction containing the deposit /// request. It must be 32 bytes. #[prost(message, optional, tag = "1")] - pub txid: ::core::option::Option, - /// Index of the deposit request UTXO. - #[prost(uint32, tag = "2")] - pub output_index: u32, + pub outpoint: ::core::option::Option, /// Whether or not the signer has accepted the deposit request. #[prost(bool, tag = "3")] pub accepted: bool, @@ -18,10 +15,10 @@ pub struct SignerDepositDecision { #[prost(bool, tag = "4")] pub can_sign: bool, } -/// Represents a decision to accept or reject a deposit request. +/// Represents a decision to accept or reject a withdrawal request. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct SignerWithdrawDecision { +pub struct SignerWithdrawalDecision { /// ID of the withdraw request. #[prost(uint64, tag = "1")] pub request_id: u64, From c805f34344fc29bc9b56820fde01385f0a1107e0 Mon Sep 17 00:00:00 2001 From: djordon Date: Sun, 6 Oct 2024 01:44:38 -0400 Subject: [PATCH 07/11] Be consistent --- signer/src/proto/convert.rs | 73 +++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index fb9fd4658..25e2588b0 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,4 +1,8 @@ -//! Conversion functions from protobufs to regular types +//! Conversion functions from a protobuf type to regular type and vice +//! verse. +//! +//! Converting to a protobuf type must be infallible, while converting from +//! a protobuf type can be fallible. //! use clarity::codec::StacksMessageCodec as _; @@ -19,8 +23,6 @@ use crate::storage::model::StacksBlockHash; use crate::storage::model::StacksPrincipal; use crate::storage::model::StacksTxId; -use crate::proto::Uint256; - /// This trait is to make it easy to handle fields of protobuf structs that /// are `None`, when they should be `Some(_)`. trait RequiredField: Sized { @@ -35,7 +37,7 @@ impl RequiredField for Option { } } -impl From<[u8; 32]> for Uint256 { +impl From<[u8; 32]> for proto::Uint256 { fn from(value: [u8; 32]) -> Self { let mut part0 = [0u8; 8]; let mut part1 = [0u8; 8]; @@ -47,7 +49,7 @@ impl From<[u8; 32]> for Uint256 { part2.copy_from_slice(&value[16..24]); part3.copy_from_slice(&value[24..32]); - Uint256 { + proto::Uint256 { bits_part0: u64::from_le_bytes(part0), bits_part1: u64::from_le_bytes(part1), bits_part2: u64::from_le_bytes(part2), @@ -56,8 +58,8 @@ impl From<[u8; 32]> for Uint256 { } } -impl From for [u8; 32] { - fn from(value: Uint256) -> Self { +impl From for [u8; 32] { + fn from(value: proto::Uint256) -> Self { let mut bytes = [0u8; 32]; bytes[..8].copy_from_slice(&value.bits_part0.to_le_bytes()); @@ -72,7 +74,7 @@ impl From for proto::PublicKey { fn from(value: PublicKey) -> Self { let (x_only, parity) = value.x_only_public_key(); proto::PublicKey { - x_only_public_key: Some(Uint256::from(x_only.serialize())), + x_only_public_key: Some(proto::Uint256::from(x_only.serialize())), parity_is_odd: parity == secp256k1::Parity::Odd, } } @@ -104,8 +106,8 @@ impl From for proto::RecoverableSignature { upper_bits.copy_from_slice(&bytes[32..]); Self { - lower_bits: Some(Uint256::from(lower_bits)), - upper_bits: Some(Uint256::from(upper_bits)), + lower_bits: Some(proto::Uint256::from(lower_bits)), + upper_bits: Some(proto::Uint256::from(upper_bits)), recovery_id: recovery_id.to_i32(), } } @@ -133,7 +135,7 @@ impl TryFrom for RecoverableSignature { impl From for proto::BitcoinTxid { fn from(value: BitcoinTxId) -> Self { proto::BitcoinTxid { - txid: Some(Uint256::from(value.into_bytes())), + txid: Some(proto::Uint256::from(value.into_bytes())), } } } @@ -149,7 +151,7 @@ impl TryFrom for BitcoinTxId { impl From for proto::BitcoinBlockHash { fn from(value: BitcoinBlockHash) -> Self { proto::BitcoinBlockHash { - block_hash: Some(Uint256::from(value.into_bytes())), + block_hash: Some(proto::Uint256::from(value.into_bytes())), } } } @@ -186,7 +188,7 @@ impl TryFrom for bitcoin::OutPoint { impl From for proto::StacksTxid { fn from(value: StacksTxId) -> Self { proto::StacksTxid { - txid: Some(Uint256::from(value.into_bytes())), + txid: Some(proto::Uint256::from(value.into_bytes())), } } } @@ -202,7 +204,7 @@ impl TryFrom for StacksTxId { impl From for proto::StacksBlockId { fn from(value: StacksBlockHash) -> Self { proto::StacksBlockId { - block_id: Some(Uint256::from(value.into_bytes())), + block_id: Some(proto::Uint256::from(value.into_bytes())), } } } @@ -331,7 +333,6 @@ impl TryFrom for StacksTransactionSignature { } } - #[cfg(test)] mod tests { use crate::keys::PrivateKey; @@ -386,22 +387,22 @@ mod tests { let bytes: [u8; 32] = Faker.fake_with_rng(&mut OsRng); let number = proto::Uint256::from(bytes); - let rount_trip_bytes = <[u8; 32]>::from(number); - assert_eq!(rount_trip_bytes, bytes); - } - - #[test_case(PhantomData::<[u8; 32]>, PhantomData::; "Uint256")] - #[test_case(PhantomData::, PhantomData::; "PublicKey")] - #[test_case(PhantomData::, PhantomData::; "BitcoinTxId")] - #[test_case(PhantomData::, PhantomData::; "BitcoinBlockHash")] - #[test_case(PhantomData::, PhantomData::; "StacksTxId")] - #[test_case(PhantomData::, PhantomData::; "StacksBlockHash")] - #[test_case(PhantomData::, PhantomData::; "StacksPrincipal")] - #[test_case(PhantomData::, PhantomData::; "SignerDepositDecision")] - #[test_case(PhantomData::, PhantomData::; "SignerWithdrawalDecision")] - #[test_case(PhantomData::, PhantomData::; "BitcoinTransactionSignAck")] - #[test_case(PhantomData::, PhantomData::; "StacksTransactionSignature")] - fn convert_protobuf_type(_: PhantomData, _: PhantomData) + let round_trip_bytes = <[u8; 32]>::from(number); + assert_eq!(round_trip_bytes, bytes); + } + + #[test_case(PhantomData::<([u8; 32], proto::Uint256)>; "Uint256")] + #[test_case(PhantomData::<(PublicKey, proto::PublicKey)>; "PublicKey")] + #[test_case(PhantomData::<(BitcoinTxId, proto::BitcoinTxid)>; "BitcoinTxId")] + #[test_case(PhantomData::<(BitcoinBlockHash, proto::BitcoinBlockHash)>; "BitcoinBlockHash")] + #[test_case(PhantomData::<(StacksTxId, proto::StacksTxid)>; "StacksTxId")] + #[test_case(PhantomData::<(StacksBlockHash, proto::StacksBlockId)>; "StacksBlockHash")] + #[test_case(PhantomData::<(StacksPrincipal, proto::StacksPrincipal)>; "StacksPrincipal")] + #[test_case(PhantomData::<(SignerDepositDecision, proto::SignerDepositDecision)>; "SignerDepositDecision")] + #[test_case(PhantomData::<(SignerWithdrawalDecision, proto::SignerWithdrawalDecision)>; "SignerWithdrawalDecision")] + #[test_case(PhantomData::<(BitcoinTransactionSignAck, proto::BitcoinTransactionSignAck)>; "BitcoinTransactionSignAck")] + #[test_case(PhantomData::<(StacksTransactionSignature, proto::StacksTransactionSignature)>; "StacksTransactionSignature")] + fn convert_protobuf_type(_: PhantomData<(T, U)>) where // `.unwrap()` requires that `E` implement `std::fmt::Debug` and // `assert_eq!` requires `PartialEq + std::fmt::Debug`. @@ -417,8 +418,8 @@ mod tests { // counterpart. We can always infallibly create U from T. let proto_original = U::from(original.clone()); - // Some other signer recieves an instance of U. This could be a - // mallicious actor or a modified version of the signer binary + // Some other signer receives an instance of U. This could be a + // malicious actor or a modified version of the signer binary // where they made some mistake, so converting back to T can fail. let original_from_proto = T::try_from(proto_original).unwrap(); // In this case, we know U was created from T correctly, so we @@ -428,9 +429,9 @@ mod tests { /// This test is identical to [`convert_protobuf_types`] tests above, /// except we cannot implement Dummy on these types. - #[test_case(PhantomData::, PhantomData::; "OutPoint")] - #[test_case(PhantomData::, PhantomData::; "RecoverableSignature")] - fn convert_protobuf_type2(_: PhantomData, _: PhantomData) + #[test_case(PhantomData::<(bitcoin::OutPoint, proto::OutPoint)>; "OutPoint")] + #[test_case(PhantomData::<(RecoverableSignature, proto::RecoverableSignature)>; "RecoverableSignature")] + fn convert_protobuf_type2(_: PhantomData<(T, U)>) where T: Dummy + TryFrom + Clone + PartialEq + std::fmt::Debug, U: From, From 57d89d4c89838f0bf0eeffb351806b0f763a7324 Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 9 Nov 2024 14:47:41 -0500 Subject: [PATCH 08/11] This needs to be here too --- signer/src/proto/convert.rs | 2 ++ signer/src/proto/mod.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index 25e2588b0..ec9dd453a 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -258,6 +258,7 @@ impl From for proto::SignerDepositDecision { vout: value.output_index, }), accepted: value.accepted, + can_sign: value.can_sign, } } } @@ -270,6 +271,7 @@ impl TryFrom for SignerDepositDecision { txid: outpoint.txid, output_index: outpoint.vout, accepted: value.accepted, + can_sign: value.can_sign, }) } } diff --git a/signer/src/proto/mod.rs b/signer/src/proto/mod.rs index 288525cb3..75964af9b 100644 --- a/signer/src/proto/mod.rs +++ b/signer/src/proto/mod.rs @@ -3,8 +3,8 @@ mod generated; pub mod convert; -pub use generated::crypto::wsts::*; pub use generated::bitcoin::*; +pub use generated::crypto::wsts::*; pub use generated::crypto::*; pub use generated::stacks::signer::v1::stacks_transaction_sign_request::*; pub use generated::stacks::signer::v1::*; From aba98988565814256b1368a753289ede5d754a15 Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 9 Nov 2024 14:48:53 -0500 Subject: [PATCH 09/11] This somehow made it in? --- .generated-sources/blocklist-api/Cargo.toml | 1 + Cargo.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/.generated-sources/blocklist-api/Cargo.toml b/.generated-sources/blocklist-api/Cargo.toml index cede6b62c..a1dfbeb67 100644 --- a/.generated-sources/blocklist-api/Cargo.toml +++ b/.generated-sources/blocklist-api/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" serde = { version = "^1.0", features = ["derive"] } serde_with = { version = "^3.8", default-features = false, features = ["base64", "std", "macros"] } serde_json = "^1.0" +serde_repr = "^0.1" url = "^2.5" uuid = { version = "^1.8", features = ["serde", "v4"] } reqwest = { version = "^0.12", features = ["json", "multipart"] } diff --git a/Cargo.lock b/Cargo.lock index 18b7da32c..616b6a52b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1050,6 +1050,7 @@ dependencies = [ "reqwest 0.12.4", "serde 1.0.203", "serde_json", + "serde_repr", "serde_with", "url", "uuid", From 5f13748a411f18daca5783217429bc858e626c1b Mon Sep 17 00:00:00 2001 From: djordon Date: Sat, 9 Nov 2024 15:02:08 -0500 Subject: [PATCH 10/11] Use a better type for protobuf required field errors --- signer/src/error.rs | 4 ++++ signer/src/proto/convert.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/signer/src/error.rs b/signer/src/error.rs index f1e66a81e..060019357 100644 --- a/signer/src/error.rs +++ b/signer/src/error.rs @@ -387,6 +387,10 @@ pub enum Error { #[error("observer dropped")] ObserverDropped, + /// A required field in a protobuf type was not set. + #[error("a required protobuf field was not set")] + RequiredProtobufFieldMissing, + /// Thrown when the recoverable signature has a public key that is /// unexpected. #[error("unexpected public key from signature. key {0}; digest: {1}")] diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index ec9dd453a..a0f94e3ee 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -33,7 +33,7 @@ trait RequiredField: Sized { impl RequiredField for Option { type Inner = T; fn required(self) -> Result { - self.ok_or(Error::TypeConversion) + self.ok_or(Error::RequiredProtobufFieldMissing) } } From ef0c639d01a55254088f96c4390cda1f840d0bd6 Mon Sep 17 00:00:00 2001 From: djordon Date: Mon, 11 Nov 2024 08:44:04 -0500 Subject: [PATCH 11/11] Add a test, remove another, update comments, and a tag ID --- protobufs/stacks/signer/v1/decisions.proto | 7 +++---- signer/src/proto/convert.rs | 21 ++++++++++--------- .../src/proto/generated/stacks.signer.v1.rs | 7 +++---- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/protobufs/stacks/signer/v1/decisions.proto b/protobufs/stacks/signer/v1/decisions.proto index b9d77856c..1edd7f2ee 100644 --- a/protobufs/stacks/signer/v1/decisions.proto +++ b/protobufs/stacks/signer/v1/decisions.proto @@ -8,14 +8,13 @@ import "stacks/common.proto"; // Represents a decision to accept or reject a deposit request. message SignerDepositDecision { - // The bitcoin transaction ID of the transaction containing the deposit - // request. It must be 32 bytes. + // The bitcoin outpoint that uniquely identifies the deposit request. bitcoin.OutPoint outpoint = 1; // Whether or not the signer has accepted the deposit request. - bool accepted = 3; + bool accepted = 2; // This specifies whether the sending signer can provide signature shares // for the associated deposit request. - bool can_sign = 4; + bool can_sign = 3; } // Represents a decision to accept or reject a withdrawal request. diff --git a/signer/src/proto/convert.rs b/signer/src/proto/convert.rs index a0f94e3ee..693ae9971 100644 --- a/signer/src/proto/convert.rs +++ b/signer/src/proto/convert.rs @@ -1,5 +1,5 @@ //! Conversion functions from a protobuf type to regular type and vice -//! verse. +//! versa. //! //! Converting to a protobuf type must be infallible, while converting from //! a protobuf type can be fallible. @@ -370,6 +370,15 @@ mod tests { } } + impl Dummy for StacksAddress { + fn dummy_with_rng(_: &Unit, rng: &mut R) -> Self { + let public_key: PublicKey = Faker.fake_with_rng(rng); + let pubkey = public_key.into(); + let mainnet: bool = Faker.fake_with_rng(rng); + StacksAddress::p2pkh(mainnet, &pubkey) + } + } + #[test] fn conversion_between_bytes_and_uint256() { let number = proto::Uint256 { @@ -384,15 +393,6 @@ mod tests { assert_eq!(round_trip_number, number); } - #[test] - fn conversion_between_uint256_and_bytes() { - let bytes: [u8; 32] = Faker.fake_with_rng(&mut OsRng); - let number = proto::Uint256::from(bytes); - - let round_trip_bytes = <[u8; 32]>::from(number); - assert_eq!(round_trip_bytes, bytes); - } - #[test_case(PhantomData::<([u8; 32], proto::Uint256)>; "Uint256")] #[test_case(PhantomData::<(PublicKey, proto::PublicKey)>; "PublicKey")] #[test_case(PhantomData::<(BitcoinTxId, proto::BitcoinTxid)>; "BitcoinTxId")] @@ -433,6 +433,7 @@ mod tests { /// except we cannot implement Dummy on these types. #[test_case(PhantomData::<(bitcoin::OutPoint, proto::OutPoint)>; "OutPoint")] #[test_case(PhantomData::<(RecoverableSignature, proto::RecoverableSignature)>; "RecoverableSignature")] + #[test_case(PhantomData::<(StacksAddress, proto::StacksAddress)>; "StacksAddress")] fn convert_protobuf_type2(_: PhantomData<(T, U)>) where T: Dummy + TryFrom + Clone + PartialEq + std::fmt::Debug, diff --git a/signer/src/proto/generated/stacks.signer.v1.rs b/signer/src/proto/generated/stacks.signer.v1.rs index ece0677b6..5895460f3 100644 --- a/signer/src/proto/generated/stacks.signer.v1.rs +++ b/signer/src/proto/generated/stacks.signer.v1.rs @@ -3,16 +3,15 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SignerDepositDecision { - /// The bitcoin transaction ID of the transaction containing the deposit - /// request. It must be 32 bytes. + /// The bitcoin outpoint that uniquely identifies the deposit request. #[prost(message, optional, tag = "1")] pub outpoint: ::core::option::Option, /// Whether or not the signer has accepted the deposit request. - #[prost(bool, tag = "3")] + #[prost(bool, tag = "2")] pub accepted: bool, /// This specifies whether the sending signer can provide signature shares /// for the associated deposit request. - #[prost(bool, tag = "4")] + #[prost(bool, tag = "3")] pub can_sign: bool, } /// Represents a decision to accept or reject a withdrawal request.