diff --git a/emily/handler/src/database/entries/deposit.rs b/emily/handler/src/database/entries/deposit.rs index 4c869e5bc..4d2b48b77 100644 --- a/emily/handler/src/database/entries/deposit.rs +++ b/emily/handler/src/database/entries/deposit.rs @@ -569,7 +569,6 @@ impl DepositUpdatePackage { mod tests { use super::*; use test_case::test_case; - use testing_emily_client::models::fulfillment; #[test] fn deposit_update_should_be_unnecessary_when_event_is_present() { diff --git a/signer/src/stacks/events.rs b/sbtc/src/events.rs similarity index 97% rename from signer/src/stacks/events.rs rename to sbtc/src/events.rs index 974990a38..0259d7d4b 100644 --- a/signer/src/stacks/events.rs +++ b/sbtc/src/events.rs @@ -11,6 +11,7 @@ use std::collections::BTreeMap; use bitcoin::hashes::Hash; +use bitcoin::hex::DisplayHex; use bitcoin::BlockHash as BitcoinBlockHash; use bitcoin::OutPoint; use bitcoin::PubkeyHash; @@ -19,8 +20,6 @@ use bitcoin::ScriptHash; use bitcoin::Txid as BitcoinTxid; use bitcoin::WitnessProgram; use bitcoin::WitnessVersion; -use bitvec::array::BitArray; -use blockstack_lib::burnchains::Txid as StacksTxid; use clarity::vm::types::CharType; use clarity::vm::types::PrincipalData; use clarity::vm::types::SequenceData; @@ -30,6 +29,18 @@ use clarity::vm::Value as ClarityValue; use secp256k1::PublicKey; use stacks_common::types::chainstate::StacksBlockId; +use std::fmt::Display; + +/// Stacks transaction identifier. Wrapper over a 32 byte array. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct StacksTxid(pub [u8; 32]); + +impl Display for StacksTxid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.0.to_hex_string(bitcoin::hex::Case::Lower)) + } +} + /// This trait adds a function for converting bytes from little-endian byte /// order into a bitcoin hash types. This is because the signers convert /// [`bitcoin::Txid`] and [`bitcoin::BlockHash`] bytes into little-endian @@ -232,7 +243,7 @@ pub struct WithdrawalAcceptEvent { /// The bitmap of how the signers voted for the withdrawal request. /// Here, a 1 (or true) implies that the signer did *not* vote to /// accept the request. - pub signer_bitmap: BitArray<[u8; 16]>, + pub signer_bitmap: u128, /// This is the outpoint for the bitcoin transaction that serviced the /// request. pub outpoint: OutPoint, @@ -263,7 +274,7 @@ pub struct WithdrawalRejectEvent { /// The bitmap of how the signers voted for the withdrawal request. /// Here, a 1 (or true) implies that the signer did *not* vote to /// accept the request. - pub signer_bitmap: BitArray<[u8; 16]>, + pub signer_bitmap: u128, } /// This is the event that is emitted from the `rotate-keys` @@ -626,7 +637,7 @@ impl RawTupleData { // This shouldn't error for the reasons noted in // [`withdrawal_create`]. request_id: u64::try_from(request_id).map_err(EventError::ClarityIntConversion)?, - signer_bitmap: BitArray::new(bitmap.to_le_bytes()), + signer_bitmap: bitmap, outpoint: OutPoint { // This shouldn't error, this is set from a proper [`Txid`] in // a contract call. @@ -677,7 +688,7 @@ impl RawTupleData { // This shouldn't error for the reasons noted in // [`withdrawal_create`]. request_id: u64::try_from(request_id).map_err(EventError::ClarityIntConversion)?, - signer_bitmap: BitArray::new(bitmap.to_le_bytes()), + signer_bitmap: bitmap, })) } @@ -733,7 +744,6 @@ mod tests { use bitcoin::key::CompressedPublicKey; use bitcoin::key::TweakedPublicKey; - use bitvec::field::BitField as _; use clarity::vm::types::ListData; use clarity::vm::types::ListTypeData; use clarity::vm::types::BUFF_33; @@ -749,24 +759,6 @@ mod tests { block_id: StacksBlockId([0; 32]), }; - #[test] - fn signer_bitmap_conversion() { - // This test checks that converting from an integer to the bitmap - // works the way that we expect. - let bitmap_number: u128 = 3; - let bitmap: BitArray<[u8; 16]> = BitArray::new(bitmap_number.to_le_bytes()); - - assert_eq!(bitmap.load_le::(), bitmap_number); - - // This is basically a test of the same thing as the above, except - // that we explicitly create the signer bitmap. - let mut bitmap: BitArray<[u8; 16]> = BitArray::ZERO; - bitmap.set(0, true); - bitmap.set(1, true); - - assert_eq!(bitmap.load_le::(), bitmap_number); - } - #[test] fn complete_deposit_event() { let amount = 123654789; @@ -802,7 +794,7 @@ mod tests { assert_eq!(event.outpoint.vout, 3); assert_eq!( event.sweep_block_hash, - BitcoinBlockHash::from_byte_array([2; 32]) + BitcoinBlockHash::from_byte_array([2; 32]), ); assert_eq!(event.sweep_block_height, 139); assert_eq!(event.sweep_txid, BitcoinTxid::from_byte_array([3; 32])); @@ -922,7 +914,7 @@ mod tests { // let res = transform_value(value, NetworkKind::Regtest).unwrap(); match RegistryEvent::try_new(value, TX_INFO).unwrap() { RegistryEvent::WithdrawalAccept(event) => { - let expected_bitmap = BitArray::<[u8; 16]>::new(bitmap.to_le_bytes()); + let expected_bitmap = bitmap; assert_eq!(event.request_id, request_id as u64); assert_eq!(event.outpoint.txid, BitcoinTxid::from_byte_array([1; 32])); assert_eq!(event.outpoint.vout, vout as u32); @@ -964,7 +956,7 @@ mod tests { // let res = transform_value(value, NetworkKind::Regtest).unwrap(); match RegistryEvent::try_new(value, TX_INFO).unwrap() { RegistryEvent::WithdrawalReject(event) => { - let expected_bitmap = BitArray::<[u8; 16]>::new(bitmap.to_le_bytes()); + let expected_bitmap = bitmap; assert_eq!(event.request_id, request_id as u64); assert_eq!(event.signer_bitmap, expected_bitmap); } diff --git a/sbtc/src/lib.rs b/sbtc/src/lib.rs index cff1a8e1a..395dfaef4 100644 --- a/sbtc/src/lib.rs +++ b/sbtc/src/lib.rs @@ -9,6 +9,7 @@ use bitcoin::XOnlyPublicKey; pub mod deposits; pub mod error; +pub mod events; #[cfg(any(test, feature = "testing"))] pub mod testing; diff --git a/signer/src/api/new_block.rs b/signer/src/api/new_block.rs index 350f4d688..d3c02a0fa 100644 --- a/signer/src/api/new_block.rs +++ b/signer/src/api/new_block.rs @@ -16,6 +16,8 @@ use emily_client::models::UpdateWithdrawalsResponse; use emily_client::models::WithdrawalParameters; use emily_client::models::WithdrawalUpdate; use futures::FutureExt; +use sbtc::events::RegistryEvent; +use sbtc::events::TxInfo; use std::sync::OnceLock; use crate::context::Context; @@ -23,19 +25,15 @@ use crate::emily_client::EmilyInteract; use crate::error::Error; use crate::metrics::Metrics; use crate::metrics::STACKS_BLOCKCHAIN; -use crate::stacks::events::CompletedDepositEvent; -use crate::stacks::events::KeyRotationEvent; -use crate::stacks::events::RegistryEvent; -use crate::stacks::events::TxInfo; -use crate::stacks::events::WithdrawalAcceptEvent; -use crate::stacks::events::WithdrawalCreateEvent; -use crate::stacks::events::WithdrawalRejectEvent; use crate::stacks::webhooks::NewBlockEvent; -use crate::storage::model::BitcoinBlockHash; +use crate::storage::model::CompletedDepositEvent; +use crate::storage::model::KeyRotationEvent; use crate::storage::model::RotateKeysTransaction; use crate::storage::model::StacksBlock; -use crate::storage::model::StacksBlockHash; use crate::storage::model::StacksTxId; +use crate::storage::model::WithdrawalAcceptEvent; +use crate::storage::model::WithdrawalCreateEvent; +use crate::storage::model::WithdrawalRejectEvent; use crate::storage::DbRead; use crate::storage::DbWrite; @@ -119,10 +117,10 @@ pub async fn new_block_handler(state: State>, body: Strin .filter(|(ev, _)| &ev.contract_identifier == registry_address && ev.topic == "print"); let stacks_chaintip = StacksBlock { - block_hash: StacksBlockHash::from(new_block_event.index_block_hash), + block_hash: new_block_event.index_block_hash.into(), block_height: new_block_event.block_height, - parent_hash: StacksBlockHash::from(new_block_event.parent_index_block_hash), - bitcoin_anchor: BitcoinBlockHash::from(new_block_event.burn_block_hash), + parent_hash: new_block_event.parent_index_block_hash.into(), + bitcoin_anchor: new_block_event.burn_block_hash.into(), }; let block_id = new_block_event.index_block_hash; @@ -132,30 +130,33 @@ pub async fn new_block_handler(state: State>, body: Strin let mut created_withdrawals = Vec::new(); for (ev, txid) in events { - let tx_info = TxInfo { txid, block_id }; + let tx_info = TxInfo { + txid: sbtc::events::StacksTxid(txid.0), + block_id, + }; let res = match RegistryEvent::try_new(ev.value, tx_info) { Ok(RegistryEvent::CompletedDeposit(event)) => { - handle_completed_deposit(&api.ctx, event, &stacks_chaintip) + handle_completed_deposit(&api.ctx, event.into(), &stacks_chaintip) .await .map(|x| completed_deposits.push(x)) } Ok(RegistryEvent::WithdrawalAccept(event)) => { - handle_withdrawal_accept(&api.ctx, event, &stacks_chaintip) + handle_withdrawal_accept(&api.ctx, event.into(), &stacks_chaintip) .await .map(|x| updated_withdrawals.push(x)) } Ok(RegistryEvent::WithdrawalReject(event)) => { - handle_withdrawal_reject(&api.ctx, event, &stacks_chaintip) + handle_withdrawal_reject(&api.ctx, event.into(), &stacks_chaintip) .await .map(|x| updated_withdrawals.push(x)) } Ok(RegistryEvent::WithdrawalCreate(event)) => { - handle_withdrawal_create(&api.ctx, event, stacks_chaintip.block_height) + handle_withdrawal_create(&api.ctx, event.into(), stacks_chaintip.block_height) .await .map(|x| created_withdrawals.push(x)) } Ok(RegistryEvent::KeyRotation(event)) => { - handle_key_rotation(&api.ctx, event, tx_info.txid.into()).await + handle_key_rotation(&api.ctx, event.into(), tx_info.txid.into()).await } Err(error) => { tracing::error!(%error, "got an error when transforming the event ClarityValue"); @@ -385,8 +386,8 @@ async fn handle_key_rotation( ) -> Result<(), Error> { let key_rotation_tx = RotateKeysTransaction { txid: stacks_txid, - address: event.new_address.into(), - aggregate_key: event.new_aggregate_pubkey.into(), + address: event.new_address, + aggregate_key: event.new_aggregate_pubkey, signer_set: event.new_keys.into_iter().map(Into::into).collect(), signatures_required: event.new_signature_threshold, }; @@ -401,7 +402,6 @@ mod tests { use super::*; use bitcoin::OutPoint; - use bitcoin::ScriptBuf; use bitvec::array::BitArray; use clarity::vm::types::PrincipalData; use emily_client::models::UpdateDepositsResponse; @@ -414,6 +414,7 @@ mod tests { use crate::storage::in_memory::Store; use crate::storage::model::DepositRequest; + use crate::storage::model::ScriptPubKey; use crate::storage::model::StacksPrincipal; use crate::testing::context::*; use crate::testing::storage::model::TestData; @@ -613,12 +614,12 @@ mod tests { let event = CompletedDepositEvent { outpoint: deposit_request.outpoint(), - txid: *stacks_txid, - block_id: *stacks_chaintip.block_hash, + txid: stacks_txid.into(), + block_id: stacks_chaintip.block_hash.into(), amount: deposit_request.amount - btc_fee, - sweep_block_hash: *bitcoin_block.block_hash, + sweep_block_hash: bitcoin_block.block_hash.into(), sweep_block_height: bitcoin_block.block_height, - sweep_txid: *txid, + sweep_txid: txid.into(), }; let expectation = DepositUpdate { bitcoin_tx_output_index: event.outpoint.vout, @@ -677,12 +678,12 @@ mod tests { let outpoint = OutPoint { txid: *txid, vout: 0 }; let event = CompletedDepositEvent { outpoint: outpoint.clone(), - txid: *stacks_txid, - block_id: *stacks_chaintip.block_hash, + txid: stacks_txid.into(), + block_id: stacks_chaintip.block_hash.into(), amount: 100, - sweep_block_hash: *bitcoin_block.block_hash, + sweep_block_hash: bitcoin_block.block_hash.into(), sweep_block_height: bitcoin_block.block_height, - sweep_txid: *txid, + sweep_txid: txid.into(), }; let res = handle_completed_deposit(&ctx, event, stacks_chaintip).await; assert!(res.is_err()); @@ -730,13 +731,13 @@ mod tests { let event = WithdrawalAcceptEvent { request_id: 1, outpoint: OutPoint { txid: *txid, vout: 0 }, - txid: *stacks_tx.txid, - block_id: *stacks_tx.block_hash, + txid: stacks_tx.txid.into(), + block_id: stacks_tx.block_hash.into(), fee: 1, signer_bitmap: BitArray::<_>::ZERO, - sweep_block_hash: *bitcoin_block.block_hash, + sweep_block_hash: bitcoin_block.block_hash.into(), sweep_block_height: bitcoin_block.block_height, - sweep_txid: *txid, + sweep_txid: txid.into(), }; // Expected struct to be added to the accepted_withdrawals vector @@ -795,12 +796,12 @@ mod tests { let event = WithdrawalCreateEvent { request_id: 1, - block_id: *stacks_first_tx.block_hash, + block_id: stacks_first_tx.block_hash.into(), amount: 100, max_fee: 1, - recipient: ScriptBuf::default(), - txid: *stacks_first_tx.txid, - sender: PrincipalData::Standard(StandardPrincipalData::transient()), + recipient: ScriptPubKey::from_bytes(vec![]), + txid: stacks_first_tx.txid, + sender: PrincipalData::Standard(StandardPrincipalData::transient()).into(), block_height: test_data.bitcoin_blocks[0].block_height, }; @@ -857,8 +858,8 @@ mod tests { let event = WithdrawalRejectEvent { request_id: 1, - block_id: *stacks_chaintip.block_hash, - txid: *test_data.stacks_transactions[0].txid, + block_id: stacks_chaintip.block_hash.into(), + txid: test_data.stacks_transactions[0].txid, signer_bitmap: BitArray::<_>::ZERO, }; @@ -898,11 +899,11 @@ mod tests { let txid: StacksTxId = fake::Faker.fake_with_rng(&mut OsRng); let event = KeyRotationEvent { - new_aggregate_pubkey: SECP256K1.generate_keypair(&mut OsRng).1, + new_aggregate_pubkey: SECP256K1.generate_keypair(&mut OsRng).1.into(), new_keys: (0..3) - .map(|_| SECP256K1.generate_keypair(&mut OsRng).1) + .map(|_| SECP256K1.generate_keypair(&mut OsRng).1.into()) .collect(), - new_address: PrincipalData::Standard(StandardPrincipalData::transient()), + new_address: PrincipalData::Standard(StandardPrincipalData::transient()).into(), new_signature_threshold: 3, }; diff --git a/signer/src/request_decider.rs b/signer/src/request_decider.rs index 90f05f300..6c22babe9 100644 --- a/signer/src/request_decider.rs +++ b/signer/src/request_decider.rs @@ -262,7 +262,7 @@ where let msg = SignerWithdrawalDecision { request_id: withdrawal_request.request_id, - block_hash: withdrawal_request.block_hash.0, + block_hash: withdrawal_request.block_hash.into_bytes(), accepted: is_accepted, txid: withdrawal_request.txid, }; diff --git a/signer/src/stacks/mod.rs b/signer/src/stacks/mod.rs index 70a04dd07..931409a1d 100644 --- a/signer/src/stacks/mod.rs +++ b/signer/src/stacks/mod.rs @@ -3,7 +3,6 @@ /// Contains an interface for interacting with a stacks node. pub mod api; pub mod contracts; -pub mod events; /// Contains structs for signing stacks transactions using the signers' /// multi-sig wallet. pub mod wallet; diff --git a/signer/src/storage/in_memory.rs b/signer/src/storage/in_memory.rs index 1fe62774a..b89d543f8 100644 --- a/signer/src/storage/in_memory.rs +++ b/signer/src/storage/in_memory.rs @@ -18,11 +18,11 @@ use crate::error::Error; use crate::keys::PublicKey; use crate::keys::PublicKeyXOnly; use crate::keys::SignerScriptPubKey as _; -use crate::stacks::events::CompletedDepositEvent; -use crate::stacks::events::WithdrawalAcceptEvent; -use crate::stacks::events::WithdrawalCreateEvent; -use crate::stacks::events::WithdrawalRejectEvent; use crate::storage::model; +use crate::storage::model::CompletedDepositEvent; +use crate::storage::model::WithdrawalAcceptEvent; +use crate::storage::model::WithdrawalCreateEvent; +use crate::storage::model::WithdrawalRejectEvent; use crate::DEPOSIT_LOCKTIME_BLOCK_BUFFER; use super::util::get_utxo; diff --git a/signer/src/storage/mod.rs b/signer/src/storage/mod.rs index ad22ec0cb..b66538a98 100644 --- a/signer/src/storage/mod.rs +++ b/signer/src/storage/mod.rs @@ -23,10 +23,10 @@ use crate::bitcoin::validation::WithdrawalRequestReport; use crate::error::Error; use crate::keys::PublicKey; use crate::keys::PublicKeyXOnly; -use crate::stacks::events::CompletedDepositEvent; -use crate::stacks::events::WithdrawalAcceptEvent; -use crate::stacks::events::WithdrawalCreateEvent; -use crate::stacks::events::WithdrawalRejectEvent; +use crate::storage::model::CompletedDepositEvent; +use crate::storage::model::WithdrawalAcceptEvent; +use crate::storage::model::WithdrawalCreateEvent; +use crate::storage::model::WithdrawalRejectEvent; /// Represents the ability to read data from the signer storage. pub trait DbRead { diff --git a/signer/src/storage/model.rs b/signer/src/storage/model.rs index 0d49414c5..dcd2a7d56 100644 --- a/signer/src/storage/model.rs +++ b/signer/src/storage/model.rs @@ -4,6 +4,7 @@ use std::collections::BTreeSet; use std::ops::Deref; use bitcoin::hashes::Hash as _; +use bitcoin::OutPoint; use bitvec::array::BitArray; use blockstack_lib::chainstate::nakamoto::NakamotoBlock; use clarity::vm::types::PrincipalData; @@ -1078,12 +1079,202 @@ pub struct BitcoinWithdrawalOutput { pub is_valid_tx: bool, } +impl From for StacksTxId { + fn from(value: sbtc::events::StacksTxid) -> Self { + Self(blockstack_lib::burnchains::Txid(value.0)) + } +} + +impl From for CompletedDepositEvent { + fn from(sbtc_event: sbtc::events::CompletedDepositEvent) -> CompletedDepositEvent { + let sweep_hash = BitcoinBlockHash::from(sbtc_event.sweep_block_hash); + let txid = StacksTxId::from(sbtc_event.txid.0); + CompletedDepositEvent { + txid, + block_id: sbtc_event.block_id.into(), + amount: sbtc_event.amount, + outpoint: sbtc_event.outpoint, + sweep_block_hash: sweep_hash, + sweep_block_height: sbtc_event.sweep_block_height, + sweep_txid: sbtc_event.sweep_txid.into(), + } + } +} + +impl From for WithdrawalAcceptEvent { + fn from(sbtc_event: sbtc::events::WithdrawalAcceptEvent) -> WithdrawalAcceptEvent { + WithdrawalAcceptEvent { + txid: sbtc_event.txid.into(), + block_id: sbtc_event.block_id.into(), + request_id: sbtc_event.request_id, + signer_bitmap: BitArray::new(sbtc_event.signer_bitmap.to_le_bytes()), + outpoint: sbtc_event.outpoint, + fee: sbtc_event.fee, + sweep_block_hash: sbtc_event.sweep_block_hash.into(), + sweep_block_height: sbtc_event.sweep_block_height, + sweep_txid: sbtc_event.sweep_txid.into(), + } + } +} + +impl From for WithdrawalRejectEvent { + fn from(sbtc_event: sbtc::events::WithdrawalRejectEvent) -> WithdrawalRejectEvent { + WithdrawalRejectEvent { + txid: sbtc_event.txid.into(), + block_id: sbtc_event.block_id.into(), + request_id: sbtc_event.request_id, + signer_bitmap: BitArray::new(sbtc_event.signer_bitmap.to_le_bytes()), + } + } +} + +impl From for WithdrawalCreateEvent { + fn from(sbtc_event: sbtc::events::WithdrawalCreateEvent) -> WithdrawalCreateEvent { + WithdrawalCreateEvent { + txid: sbtc_event.txid.into(), + block_id: sbtc_event.block_id.into(), + request_id: sbtc_event.request_id, + amount: sbtc_event.amount, + sender: sbtc_event.sender.into(), + recipient: sbtc_event.recipient.into(), + max_fee: sbtc_event.max_fee, + block_height: sbtc_event.block_height, + } + } +} + +impl From for KeyRotationEvent { + fn from(sbtc_event: sbtc::events::KeyRotationEvent) -> KeyRotationEvent { + KeyRotationEvent { + new_keys: sbtc_event + .new_keys + .into_iter() + .map(|key| key.into()) + .collect(), + new_address: sbtc_event.new_address.into(), + new_aggregate_pubkey: sbtc_event.new_aggregate_pubkey.into(), + new_signature_threshold: sbtc_event.new_signature_threshold, + } + } +} + +/// This is the event that is emitted from the `rotate-keys` +/// public function in the sbtc-registry smart contract. +#[derive(Debug, Clone)] +pub struct KeyRotationEvent { + /// The new set of public keys for all known signers during this + /// PoX cycle. + pub new_keys: Vec, + /// The address that deployed the contract. + pub new_address: StacksPrincipal, + /// The new aggregate key created by combining the above public keys. + pub new_aggregate_pubkey: PublicKey, + /// The number of signatures required for the multi-sig wallet. + pub new_signature_threshold: u16, +} + +/// This is the event that is emitted from the `create-withdrawal-request` +/// public function in sbtc-registry smart contract. +#[derive(Debug, Clone)] +pub struct CompletedDepositEvent { + /// The transaction id of the stacks transaction that generated this + /// event. + pub txid: StacksTxId, + /// The block ID of the block for this event. + pub block_id: StacksBlockHash, + /// This is the amount of sBTC to mint to the intended recipient. + pub amount: u64, + /// This is the outpoint of the original bitcoin deposit transaction. + pub outpoint: OutPoint, + /// The bitcoin block hash where the sweep transaction was included. + pub sweep_block_hash: BitcoinBlockHash, + /// The bitcoin block height where the sweep transaction was included. + pub sweep_block_height: u64, + /// The transaction id of the bitcoin transaction that fulfilled the + /// deposit. + pub sweep_txid: BitcoinTxId, +} + +/// This is the event that is emitted from the `create-withdrawal-request` +/// public function in sbtc-registry smart contract. +#[derive(Debug, Clone)] +pub struct WithdrawalCreateEvent { + /// The transaction id of the stacks transaction that generated this + /// event. + pub txid: StacksTxId, + /// The block ID of the block for this event. + pub block_id: StacksBlockHash, + /// This is the unique identifier of the withdrawal request. + pub request_id: u64, + /// This is the amount of sBTC that is locked and requested to be + /// withdrawal as sBTC. + pub amount: u64, + /// This is the principal who has their sBTC locked up. + pub sender: StacksPrincipal, + /// This is the address to send the BTC to when fulfilling the + /// withdrawal request. + pub recipient: ScriptPubKey, + /// This is the maximum amount of BTC "spent" to the miners for the + /// transaction fee. + pub max_fee: u64, + /// The block height of the bitcoin blockchain when the stacks + /// transaction that emitted this event was executed. + pub block_height: u64, +} +/// This is the event that is emitted from the `complete-withdrawal-accept` +/// public function in sbtc-registry smart contract. +#[derive(Debug, Clone)] +pub struct WithdrawalAcceptEvent { + /// The transaction id of the stacks transaction that generated this + /// event. + pub txid: StacksTxId, + /// The block ID of the block for this event. + pub block_id: StacksBlockHash, + /// This is the unique identifier of the withdrawal request. + pub request_id: u64, + /// The bitmap of how the signers voted for the withdrawal request. + /// Here, a 1 (or true) implies that the signer did *not* vote to + /// accept the request. + pub signer_bitmap: BitArray<[u8; 16]>, + /// This is the outpoint for the bitcoin transaction that serviced the + /// request. + pub outpoint: OutPoint, + /// This is the fee that was spent to the bitcoin miners to confirm the + /// withdrawal request. + pub fee: u64, + /// The bitcoin block hash where the sweep transaction was included. + pub sweep_block_hash: BitcoinBlockHash, + /// The bitcoin block height where the sweep transaction was included. + pub sweep_block_height: u64, + /// The transaction id of the bitcoin transaction that fulfilled the + /// withdrawal request. + pub sweep_txid: BitcoinTxId, +} + +/// This is the event that is emitted from the `complete-withdrawal-reject` +/// public function in sbtc-registry smart contract. +#[derive(Debug, Clone)] +pub struct WithdrawalRejectEvent { + /// The transaction id of the stacks transaction that generated this + /// event. + pub txid: StacksTxId, + /// The block ID of the block for this event. + pub block_id: StacksBlockHash, + /// This is the unique identifier of user created the withdrawal + /// request. + pub request_id: u64, + /// The bitmap of how the signers voted for the withdrawal request. + /// Here, a 1 (or true) implies that the signer did *not* vote to + /// accept the request. + pub signer_bitmap: BitArray<[u8; 16]>, +} + #[cfg(test)] mod tests { use fake::Fake; use rand::SeedableRng; - use crate::stacks::events::FromLittleEndianOrder; + use sbtc::events::FromLittleEndianOrder; use super::*; diff --git a/signer/src/storage/postgres.rs b/signer/src/storage/postgres.rs index 3a72d939e..5075799b3 100644 --- a/signer/src/storage/postgres.rs +++ b/signer/src/storage/postgres.rs @@ -22,12 +22,12 @@ use crate::bitcoin::validation::WithdrawalRequestReport; use crate::error::Error; use crate::keys::PublicKey; use crate::keys::PublicKeyXOnly; -use crate::stacks::events::CompletedDepositEvent; -use crate::stacks::events::WithdrawalAcceptEvent; -use crate::stacks::events::WithdrawalCreateEvent; -use crate::stacks::events::WithdrawalRejectEvent; use crate::storage::model; +use crate::storage::model::CompletedDepositEvent; use crate::storage::model::TransactionType; +use crate::storage::model::WithdrawalAcceptEvent; +use crate::storage::model::WithdrawalCreateEvent; +use crate::storage::model::WithdrawalRejectEvent; use crate::DEPOSIT_LOCKTIME_BLOCK_BUFFER; use crate::MAX_REORG_BLOCK_COUNT; @@ -2442,8 +2442,8 @@ impl super::DbWrite for PgStore { ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", ) - .bind(event.txid.0) - .bind(event.block_id.0) + .bind(event.txid) + .bind(event.block_id) .bind(i64::try_from(event.amount).map_err(Error::ConversionDatabaseInt)?) .bind(event.outpoint.txid.to_byte_array()) .bind(i64::from(event.outpoint.vout)) @@ -2475,8 +2475,8 @@ impl super::DbWrite for PgStore { ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", ) - .bind(event.txid.0) - .bind(event.block_id.0) + .bind(event.txid) + .bind(event.block_id) .bind(i64::try_from(event.request_id).map_err(Error::ConversionDatabaseInt)?) .bind(i64::try_from(event.amount).map_err(Error::ConversionDatabaseInt)?) .bind(event.sender.to_string()) @@ -2510,8 +2510,8 @@ impl super::DbWrite for PgStore { ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", ) - .bind(event.txid.0) - .bind(event.block_id.0) + .bind(event.txid) + .bind(event.block_id) .bind(i64::try_from(event.request_id).map_err(Error::ConversionDatabaseInt)?) .bind(event.signer_bitmap.into_inner()) .bind(event.outpoint.txid.to_byte_array()) @@ -2541,8 +2541,8 @@ impl super::DbWrite for PgStore { ) VALUES ($1, $2, $3, $4)", ) - .bind(event.txid.0) - .bind(event.block_id.0) + .bind(event.txid) + .bind(event.block_id) .bind(i64::try_from(event.request_id).map_err(Error::ConversionDatabaseInt)?) .bind(event.signer_bitmap.into_inner()) .execute(&self.0) diff --git a/signer/src/testing/dummy.rs b/signer/src/testing/dummy.rs index 12783a4a7..baa670460 100644 --- a/signer/src/testing/dummy.rs +++ b/signer/src/testing/dummy.rs @@ -12,7 +12,6 @@ use bitcoin::TapSighash; use bitcoin::TxIn; use bitcoin::TxOut; use bitvec::array::BitArray; -use blockstack_lib::burnchains::Txid as StacksTxid; use blockstack_lib::chainstate::{nakamoto, stacks}; use clarity::util::secp256k1::Secp256k1PublicKey; use fake::Fake; @@ -40,11 +39,11 @@ use crate::stacks::contracts::AcceptWithdrawalV1; use crate::stacks::contracts::CompleteDepositV1; use crate::stacks::contracts::RejectWithdrawalV1; use crate::stacks::contracts::RotateKeysV1; -use crate::stacks::events::CompletedDepositEvent; -use crate::stacks::events::WithdrawalAcceptEvent; -use crate::stacks::events::WithdrawalCreateEvent; -use crate::stacks::events::WithdrawalRejectEvent; use crate::storage::model; +use crate::storage::model::CompletedDepositEvent; +use crate::storage::model::WithdrawalAcceptEvent; +use crate::storage::model::WithdrawalCreateEvent; +use crate::storage::model::WithdrawalRejectEvent; use crate::codec::Encode; use crate::storage::model::BitcoinBlockHash; @@ -303,8 +302,8 @@ impl fake::Dummy for WithdrawalAcceptEvent { fn dummy_with_rng(config: &fake::Faker, rng: &mut R) -> Self { let bitmap = rng.next_u64() as u128; WithdrawalAcceptEvent { - txid: blockstack_lib::burnchains::Txid(config.fake_with_rng(rng)), - block_id: stacks_common::types::chainstate::StacksBlockId(config.fake_with_rng(rng)), + txid: config.fake_with_rng(rng), + block_id: config.fake_with_rng(rng), request_id: rng.next_u32() as u64, signer_bitmap: BitArray::new(bitmap.to_le_bytes()), outpoint: OutPoint { @@ -312,9 +311,9 @@ impl fake::Dummy for WithdrawalAcceptEvent { vout: rng.next_u32(), }, fee: rng.next_u32() as u64, - sweep_block_hash: block_hash(config, rng), + sweep_block_hash: config.fake_with_rng(rng), sweep_block_height: rng.next_u32() as u64, - sweep_txid: txid(config, rng), + sweep_txid: config.fake_with_rng(rng), } } } @@ -323,8 +322,8 @@ impl fake::Dummy for WithdrawalRejectEvent { fn dummy_with_rng(config: &fake::Faker, rng: &mut R) -> Self { let bitmap = rng.next_u64() as u128; WithdrawalRejectEvent { - txid: blockstack_lib::burnchains::Txid(config.fake_with_rng(rng)), - block_id: stacks_common::types::chainstate::StacksBlockId(config.fake_with_rng(rng)), + txid: config.fake_with_rng(rng), + block_id: config.fake_with_rng(rng), request_id: rng.next_u32() as u64, signer_bitmap: BitArray::new(bitmap.to_le_bytes()), } @@ -334,12 +333,12 @@ impl fake::Dummy for WithdrawalRejectEvent { impl fake::Dummy for WithdrawalCreateEvent { fn dummy_with_rng(config: &fake::Faker, rng: &mut R) -> Self { WithdrawalCreateEvent { - txid: StacksTxid(config.fake_with_rng(rng)), - block_id: stacks_common::types::chainstate::StacksBlockId(config.fake_with_rng(rng)), + txid: config.fake_with_rng(rng), + block_id: config.fake_with_rng(rng), request_id: rng.next_u32() as u64, amount: rng.next_u32() as u64, - sender: config.fake_with_rng::(rng).into(), - recipient: config.fake_with_rng::(rng).into(), + sender: config.fake_with_rng(rng), + recipient: config.fake_with_rng::(rng), max_fee: rng.next_u32() as u64, block_height: rng.next_u32() as u64, } @@ -349,16 +348,16 @@ impl fake::Dummy for WithdrawalCreateEvent { impl fake::Dummy for CompletedDepositEvent { fn dummy_with_rng(config: &fake::Faker, rng: &mut R) -> Self { CompletedDepositEvent { - txid: blockstack_lib::burnchains::Txid(config.fake_with_rng(rng)), - block_id: stacks_common::types::chainstate::StacksBlockId(config.fake_with_rng(rng)), + txid: config.fake_with_rng(rng), + block_id: config.fake_with_rng(rng), outpoint: OutPoint { txid: txid(config, rng), vout: rng.next_u32(), }, amount: rng.next_u32() as u64, - sweep_block_hash: block_hash(config, rng), + sweep_block_hash: config.fake_with_rng(rng), sweep_block_height: rng.next_u32() as u64, - sweep_txid: txid(config, rng), + sweep_txid: config.fake_with_rng(rng), } } } diff --git a/signer/tests/integration/postgres.rs b/signer/tests/integration/postgres.rs index 365bdf8cc..df8ab517b 100644 --- a/signer/tests/integration/postgres.rs +++ b/signer/tests/integration/postgres.rs @@ -33,22 +33,22 @@ use signer::stacks::contracts::CompleteDepositV1; use signer::stacks::contracts::RejectWithdrawalV1; use signer::stacks::contracts::ReqContext; use signer::stacks::contracts::RotateKeysV1; -use signer::stacks::events::CompletedDepositEvent; -use signer::stacks::events::WithdrawalAcceptEvent; -use signer::stacks::events::WithdrawalCreateEvent; -use signer::stacks::events::WithdrawalRejectEvent; use signer::storage; use signer::storage::model; use signer::storage::model::BitcoinBlockHash; use signer::storage::model::BitcoinTxId; use signer::storage::model::BitcoinTxSigHash; use signer::storage::model::BitcoinWithdrawalOutput; +use signer::storage::model::CompletedDepositEvent; use signer::storage::model::EncryptedDkgShares; use signer::storage::model::QualifiedRequestId; use signer::storage::model::ScriptPubKey; use signer::storage::model::StacksBlock; use signer::storage::model::StacksBlockHash; use signer::storage::model::StacksTxId; +use signer::storage::model::WithdrawalAcceptEvent; +use signer::storage::model::WithdrawalCreateEvent; +use signer::storage::model::WithdrawalRejectEvent; use signer::storage::model::WithdrawalSigner; use signer::storage::postgres::PgStore; use signer::storage::DbRead; @@ -1183,8 +1183,8 @@ async fn writing_completed_deposit_requests_postgres() { let (txid, block_id, amount, bitcoin_txid, vout) = db_event.pop().unwrap(); - assert_eq!(txid, event.txid.0); - assert_eq!(block_id, event.block_id.0); + assert_eq!(txid, event.txid.into_bytes()); + assert_eq!(block_id, event.block_id.into_bytes()); assert_eq!(amount as u64, event.amount); assert_eq!(bitcoin_txid, event.outpoint.txid.to_byte_array()); assert_eq!(vout as u32, event.outpoint.vout); @@ -1203,7 +1203,10 @@ async fn writing_withdrawal_create_requests_postgres() { let event: WithdrawalCreateEvent = fake::Faker.fake_with_rng(&mut rng); // Let's see if we can write these rows to the database. - store.write_withdrawal_create_event(&event).await.unwrap(); + store + .write_withdrawal_create_event(&event.clone().into()) + .await + .unwrap(); let mut db_event = sqlx::query_as::<_, ([u8; 32], [u8; 32], i64, i64, String, Vec, i64, i64)>( r#" @@ -1226,8 +1229,8 @@ async fn writing_withdrawal_create_requests_postgres() { let (txid, block_id, request_id, amount, sender, recipient, max_fee, block_height) = db_event.pop().unwrap(); - assert_eq!(txid, event.txid.0); - assert_eq!(block_id, event.block_id.0); + assert_eq!(txid, event.txid.into_bytes()); + assert_eq!(block_id, event.block_id.into_bytes()); assert_eq!(request_id as u64, event.request_id); assert_eq!(amount as u64, event.amount); assert_eq!(sender, event.sender.to_string()); @@ -1270,8 +1273,8 @@ async fn writing_withdrawal_accept_requests_postgres() { let (txid, block_id, request_id, bitmap, bitcoin_txid, vout, fee) = db_event.pop().unwrap(); - assert_eq!(txid, event.txid.0); - assert_eq!(block_id, event.block_id.0); + assert_eq!(txid, event.txid.into_bytes()); + assert_eq!(block_id, event.block_id.into_bytes()); assert_eq!(request_id as u64, event.request_id); assert_eq!(bitmap, event.signer_bitmap.into_inner()); assert_eq!(bitcoin_txid, event.outpoint.txid.to_byte_array()); @@ -1309,8 +1312,8 @@ async fn writing_withdrawal_reject_requests_postgres() { let (txid, block_id, request_id, bitmap) = db_event.pop().unwrap(); - assert_eq!(txid, event.txid.0); - assert_eq!(block_id, event.block_id.0); + assert_eq!(txid, event.txid.into_bytes()); + assert_eq!(block_id, event.block_id.into_bytes()); assert_eq!(request_id as u64, event.request_id); assert_eq!(bitmap, event.signer_bitmap.into_inner()); @@ -2071,24 +2074,24 @@ async fn get_swept_deposit_requests_does_not_return_deposit_requests_with_respon // For `setup_canonical`, the event block is on the canonical chain let event = CompletedDepositEvent { txid: fake::Faker.fake_with_rng::(&mut rng).into(), - block_id: *setup_canonical_event_block.block_hash, + block_id: setup_canonical_event_block.block_hash.into(), amount: setup_canonical.deposit_request.amount, outpoint: setup_canonical.deposit_request.outpoint, - sweep_block_hash: setup_canonical.deposit_block_hash, + sweep_block_hash: setup_canonical.deposit_block_hash.into(), sweep_block_height: 42, - sweep_txid: setup_canonical.deposit_request.outpoint.txid, + sweep_txid: setup_canonical.deposit_request.outpoint.txid.into(), }; db.write_completed_deposit_event(&event).await.unwrap(); // For `setup_fork`, the event block is not on the canonical chain let event = CompletedDepositEvent { txid: fake::Faker.fake_with_rng::(&mut rng).into(), - block_id: *setup_fork_event_block.block_hash, + block_id: setup_fork_event_block.block_hash.into(), amount: setup_fork.deposit_request.amount, outpoint: setup_fork.deposit_request.outpoint, - sweep_block_hash: setup_fork.deposit_block_hash, + sweep_block_hash: setup_fork.deposit_block_hash.into(), sweep_block_height: 42, - sweep_txid: setup_fork.deposit_request.outpoint.txid, + sweep_txid: setup_fork.deposit_request.outpoint.txid.into(), }; db.write_completed_deposit_event(&event).await.unwrap(); @@ -2115,12 +2118,12 @@ async fn get_swept_deposit_requests_does_not_return_deposit_requests_with_respon let event = CompletedDepositEvent { txid: fake::Faker.fake_with_rng::(&mut rng).into(), - block_id: *setup_fork_event_block.block_hash, + block_id: setup_fork_event_block.block_hash.into(), amount: setup_fork.deposit_request.amount, outpoint: setup_fork.deposit_request.outpoint, - sweep_block_hash: setup_fork.deposit_block_hash, + sweep_block_hash: setup_fork.deposit_block_hash.into(), sweep_block_height: 42, - sweep_txid: setup_fork.deposit_request.outpoint.txid, + sweep_txid: setup_fork.deposit_request.outpoint.txid.into(), }; db.write_completed_deposit_event(&event).await.unwrap(); @@ -2265,12 +2268,12 @@ async fn get_swept_deposit_requests_response_tx_reorged() { let event = CompletedDepositEvent { txid: fake::Faker.fake_with_rng::(&mut rng).into(), - block_id: *original_event_block.block_hash, + block_id: original_event_block.block_hash.into(), amount: setup.deposit_request.amount, outpoint: setup.deposit_request.outpoint, - sweep_block_hash: setup.deposit_block_hash, + sweep_block_hash: setup.deposit_block_hash.into(), sweep_block_height: 42, - sweep_txid: setup.deposit_request.outpoint.txid, + sweep_txid: setup.deposit_request.outpoint.txid.into(), }; db.write_completed_deposit_event(&event).await.unwrap(); diff --git a/signer/tests/integration/stacks_events_observer.rs b/signer/tests/integration/stacks_events_observer.rs index d07daaab4..b9e402a11 100644 --- a/signer/tests/integration/stacks_events_observer.rs +++ b/signer/tests/integration/stacks_events_observer.rs @@ -15,6 +15,8 @@ use emily_client::models::Status; use emily_client::models::WithdrawalParameters; use fake::Fake; use rand::rngs::OsRng; +use sbtc::events::RegistryEvent; +use sbtc::events::TxInfo; use sbtc::testing::deposits::TxSetup; use signer::api::new_block_handler; use signer::api::ApiState; @@ -22,8 +24,6 @@ use signer::bitcoin::MockBitcoinInteract; use signer::context::Context; use signer::emily_client::EmilyClient; use signer::stacks::api::MockStacksInteract; -use signer::stacks::events::RegistryEvent; -use signer::stacks::events::TxInfo; use signer::stacks::webhooks::NewBlockEvent; use signer::storage::in_memory::Store; use signer::storage::model::DepositRequest; @@ -80,7 +80,7 @@ where let new_block_event = serde_json::from_str::(body).unwrap(); let deposit_event = new_block_event.events.first().unwrap(); let tx_info = TxInfo { - txid: deposit_event.txid.clone(), + txid: sbtc::events::StacksTxid(deposit_event.txid.0.clone()), block_id: new_block_event.index_block_hash, }; let deposit_event = deposit_event.contract_event.as_ref().unwrap();