diff --git a/core/src/actor.rs b/core/src/actor.rs index 5657e4dc4..76f6cd06d 100644 --- a/core/src/actor.rs +++ b/core/src/actor.rs @@ -1,5 +1,5 @@ +use crate::builder::transaction::TxHandler; use crate::errors::BridgeError; -use crate::transaction_builder::TxHandler; use crate::utils; use bitcoin::sighash::SighashCache; use bitcoin::taproot::LeafVersion; @@ -217,7 +217,7 @@ impl Actor { #[cfg(test)] mod tests { use super::Actor; - use crate::transaction_builder::TxHandler; + use crate::builder::transaction::TxHandler; use bitcoin::{ absolute::Height, transaction::Version, Amount, Network, OutPoint, Transaction, TxIn, TxOut, }; diff --git a/core/src/aggregator.rs b/core/src/aggregator.rs index aba6d5306..da48b45fd 100644 --- a/core/src/aggregator.rs +++ b/core/src/aggregator.rs @@ -1,5 +1,6 @@ use crate::{ actor::Actor, + builder, config::BridgeConfig, errors::BridgeError, musig2::{ @@ -7,7 +8,6 @@ use crate::{ MuSigPartialSignature, MuSigPubNonce, }, traits::rpc::AggregatorServer, - transaction_builder, utils::handle_taproot_witness_new, ByteArray32, ByteArray66, EVMAddress, UTXO, }; @@ -56,7 +56,7 @@ impl Aggregator { agg_nonce: &MuSigAggNonce, partial_sigs: Vec, ) -> Result<[u8; 64], BridgeError> { - let mut tx = transaction_builder::create_slash_or_take_tx( + let mut tx = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo, operator_xonly_pk, @@ -102,7 +102,7 @@ impl Aggregator { agg_nonce: &MuSigAggNonce, partial_sigs: Vec, ) -> Result<[u8; 64], BridgeError> { - let move_tx = transaction_builder::create_move_tx( + let move_tx = builder::transaction::create_move_tx( deposit_outpoint, self.nofn_xonly_pk, self.config.bridge_amount_sats, @@ -112,7 +112,7 @@ impl Aggregator { txid: move_tx.compute_txid(), vout: 0, }; - let slash_or_take_tx_handler = transaction_builder::create_slash_or_take_tx( + let slash_or_take_tx_handler = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo, *operator_xonly_pk, @@ -135,7 +135,7 @@ impl Aggregator { // serde_json::to_string(&slash_or_take_utxo)? // ); - let mut tx_handler = transaction_builder::create_operator_takes_tx( + let mut tx_handler = builder::transaction::create_operator_takes_tx( bridge_fund_outpoint, slash_or_take_utxo, *operator_xonly_pk, @@ -175,7 +175,7 @@ impl Aggregator { agg_nonce: &MuSigAggNonce, partial_sigs: Vec, ) -> Result<[u8; 64], BridgeError> { - let mut tx = transaction_builder::create_move_tx_handler( + let mut tx = builder::transaction::create_move_tx_handler( deposit_outpoint, evm_address, recovery_taproot_address, @@ -300,7 +300,7 @@ impl Aggregator { let move_tx_sig = secp256k1::schnorr::Signature::from_slice(&agg_move_tx_final_sig)?; - let mut move_tx_handler = transaction_builder::create_move_tx_handler( + let mut move_tx_handler = builder::transaction::create_move_tx_handler( deposit_outpoint, evm_address, &recovery_taproot_address, diff --git a/core/src/builder/address.rs b/core/src/builder/address.rs new file mode 100644 index 000000000..66f28197e --- /dev/null +++ b/core/src/builder/address.rs @@ -0,0 +1,152 @@ +use crate::builder; +use crate::{utils, EVMAddress}; +use bitcoin::address::NetworkUnchecked; +use bitcoin::{ + taproot::{TaprootBuilder, TaprootSpendInfo}, + Address, ScriptBuf, +}; +use secp256k1::XOnlyPublicKey; + +/// Creates a taproot address with either key path spend or script spend path +/// addresses. This depends on given arguments. +/// +/// # Arguments +/// +/// - `scripts`: If empty, script will be key path spend +/// - `internal_key`: If not given, will be defaulted to an unspendable x-only public key +/// - `network`: Bitcoin network +/// +/// # Returns +/// +/// - [`Address`]: Generated taproot address +/// - [`TaprootSpendInfo`]: Taproot spending information +/// +/// # Panics +/// +/// Will panic if some of the operations have invalid paramaters. +pub fn create_taproot_address( + scripts: &[ScriptBuf], + internal_key: Option, + network: bitcoin::Network, +) -> (Address, TaprootSpendInfo) { + let n = scripts.len(); + + let taproot_builder = if n == 0 { + TaprootBuilder::new() + } else if n > 1 { + let m: u8 = ((n - 1).ilog2() + 1) as u8; // m = ceil(log(n)) + let k = 2_usize.pow(m.into()) - n; + (0..n).fold(TaprootBuilder::new(), |acc, i| { + acc.add_leaf(m - ((i >= n - k) as u8), scripts[i].clone()) + .unwrap() + }) + } else { + TaprootBuilder::new() + .add_leaf(0, scripts[0].clone()) + .unwrap() + }; + + let tree_info = match internal_key { + Some(xonly_pk) => taproot_builder.finalize(&utils::SECP, xonly_pk).unwrap(), + None => taproot_builder + .finalize(&utils::SECP, *utils::UNSPENDABLE_XONLY_PUBKEY) + .unwrap(), + }; + + let taproot_address = match internal_key { + Some(xonly_pk) => Address::p2tr(&utils::SECP, xonly_pk, tree_info.merkle_root(), network), + None => Address::p2tr( + &utils::SECP, + *utils::UNSPENDABLE_XONLY_PUBKEY, + tree_info.merkle_root(), + network, + ), + }; + + (taproot_address, tree_info) +} + +/// Generates a deposit address for the user. Funds can be spend by N-of-N or +/// user can take after specified time. +/// +/// # Parameters +/// +/// - `nofn_xonly_pk`: N-of-N x-only public key of the depositor +/// - `recovery_taproot_address`: User's x-only public key that can be used to +/// take funds after some time +/// - `user_evm_address`: User's EVM address. +/// - `amount`: Amount to deposit (in sats) +/// - `network`: Bitcoin network to work on +/// - `user_takes_after`: User can take the funds back, after this amounts of +/// blocks have passed +/// +/// # Returns +/// +/// - [`Address`]: Deposit taproot Bitcoin address +/// - [`TaprootSpendInfo`]: Deposit address's taproot spending information +/// +/// # Panics +/// +/// Panics if given parameters are malformed. +pub fn generate_deposit_address( + nofn_xonly_pk: XOnlyPublicKey, + recovery_taproot_address: &Address, + user_evm_address: EVMAddress, + amount: u64, + network: bitcoin::Network, + user_takes_after: u32, +) -> (Address, TaprootSpendInfo) { + let deposit_script = + builder::script::create_deposit_script(nofn_xonly_pk, user_evm_address, amount); + + let recovery_script_pubkey = recovery_taproot_address + .clone() + .assume_checked() + .script_pubkey(); + let recovery_extracted_xonly_pk = + XOnlyPublicKey::from_slice(&recovery_script_pubkey.as_bytes()[2..34]).unwrap(); + + let script_timelock = builder::script::generate_relative_timelock_script( + recovery_extracted_xonly_pk, + user_takes_after, + ); + + create_taproot_address(&[deposit_script, script_timelock], None, network) +} + +/// Shorthand function for creating a MuSig2 taproot address: No scripts and +/// `nofn_xonly_pk` as the internal key. +/// +/// # Returns +/// +/// See [`create_taproot_address`]. +/// +/// - [`Address`]: MuSig2 taproot Bitcoin address +/// - [`TaprootSpendInfo`]: MuSig2 address's taproot spending information +pub fn create_musig2_address( + nofn_xonly_pk: XOnlyPublicKey, + network: bitcoin::Network, +) -> (Address, TaprootSpendInfo) { + create_taproot_address(&[], Some(nofn_xonly_pk), network) +} + +/// Creates a kickoff taproot address with multisig script. +/// +/// # Returns +/// +/// See [`create_taproot_address`]. +/// +/// - [`Address`]: Kickoff taproot Bitcoin address +/// - [`TaprootSpendInfo`]: Kickoff address's taproot spending information +pub fn create_kickoff_address( + nofn_xonly_pk: XOnlyPublicKey, + operator_xonly_pk: XOnlyPublicKey, + network: bitcoin::Network, +) -> (Address, TaprootSpendInfo) { + let musig2_and_operator_script = builder::script::create_musig2_and_operator_multisig_script( + nofn_xonly_pk, + operator_xonly_pk, + ); + + create_taproot_address(&[musig2_and_operator_script], None, network) +} diff --git a/core/src/builder/mod.rs b/core/src/builder/mod.rs new file mode 100644 index 000000000..3f0f16200 --- /dev/null +++ b/core/src/builder/mod.rs @@ -0,0 +1,3 @@ +pub mod address; +pub mod script; +pub mod transaction; diff --git a/core/src/script_builder.rs b/core/src/builder/script.rs similarity index 100% rename from core/src/script_builder.rs rename to core/src/builder/script.rs diff --git a/core/src/transaction_builder.rs b/core/src/builder/transaction.rs similarity index 67% rename from core/src/transaction_builder.rs rename to core/src/builder/transaction.rs index 63d07648d..bc9961ef7 100644 --- a/core/src/transaction_builder.rs +++ b/core/src/builder/transaction.rs @@ -1,14 +1,13 @@ //! # Transaction Builder -use crate::{script_builder, utils, EVMAddress, UTXO}; +use crate::builder; +use crate::{utils, EVMAddress, UTXO}; use bitcoin::address::NetworkUnchecked; use bitcoin::hashes::Hash; use bitcoin::script::PushBytesBuf; use bitcoin::Transaction; use bitcoin::{ - absolute, - taproot::{TaprootBuilder, TaprootSpendInfo}, - Address, Amount, OutPoint, ScriptBuf, TxIn, TxOut, Witness, + absolute, taproot::TaprootSpendInfo, Address, Amount, OutPoint, ScriptBuf, TxIn, TxOut, Witness, }; use secp256k1::XOnlyPublicKey; @@ -26,152 +25,6 @@ pub const SLASH_OR_TAKE_TX_MIN_RELAY_FEE: u64 = 240; pub const OPERATOR_TAKES_TX_MIN_RELAY_FEE: u64 = 230; pub const KICKOFF_UTXO_AMOUNT_SATS: u64 = 100_000; -// Address Builders ------------------------------------------------------------ - -/// Creates a taproot address with either key path spend or script spend path -/// addresses. This depends on given arguments. -/// -/// # Arguments -/// -/// - `scripts`: If empty, script will be key path spend -/// - `internal_key`: If not given, will be defaulted to an unspendable x-only public key -/// - `network`: Bitcoin network -/// -/// # Returns -/// -/// - [`Address`]: Generated taproot address -/// - [`TaprootSpendInfo`]: Taproot spending information -/// -/// # Panics -/// -/// Will panic if some of the operations have invalid paramaters. -pub fn create_taproot_address( - scripts: &[ScriptBuf], - internal_key: Option, - network: bitcoin::Network, -) -> (Address, TaprootSpendInfo) { - let n = scripts.len(); - - let taproot_builder = if n == 0 { - TaprootBuilder::new() - } else if n > 1 { - let m: u8 = ((n - 1).ilog2() + 1) as u8; // m = ceil(log(n)) - let k = 2_usize.pow(m.into()) - n; - (0..n).fold(TaprootBuilder::new(), |acc, i| { - acc.add_leaf(m - ((i >= n - k) as u8), scripts[i].clone()) - .unwrap() - }) - } else { - TaprootBuilder::new() - .add_leaf(0, scripts[0].clone()) - .unwrap() - }; - - let tree_info = match internal_key { - Some(xonly_pk) => taproot_builder.finalize(&utils::SECP, xonly_pk).unwrap(), - None => taproot_builder - .finalize(&utils::SECP, *utils::UNSPENDABLE_XONLY_PUBKEY) - .unwrap(), - }; - - let taproot_address = match internal_key { - Some(xonly_pk) => Address::p2tr(&utils::SECP, xonly_pk, tree_info.merkle_root(), network), - None => Address::p2tr( - &utils::SECP, - *utils::UNSPENDABLE_XONLY_PUBKEY, - tree_info.merkle_root(), - network, - ), - }; - - (taproot_address, tree_info) -} - -/// Generates a deposit address for the user. Funds can be spend by N-of-N or -/// user can take after specified time. -/// -/// # Parameters -/// -/// - `nofn_xonly_pk`: N-of-N x-only public key of the depositor -/// - `recovery_taproot_address`: User's x-only public key that can be used to -/// take funds after some time -/// - `user_evm_address`: User's EVM address. -/// - `amount`: Amount to deposit (in sats) -/// - `network`: Bitcoin network to work on -/// - `user_takes_after`: User can take the funds back, after this amounts of -/// blocks have passed -/// -/// # Returns -/// -/// - [`Address`]: Deposit taproot Bitcoin address -/// - [`TaprootSpendInfo`]: Deposit address's taproot spending information -/// -/// # Panics -/// -/// Panics if given parameters are malformed. -pub fn generate_deposit_address( - nofn_xonly_pk: XOnlyPublicKey, - recovery_taproot_address: &Address, - user_evm_address: EVMAddress, - amount: u64, - network: bitcoin::Network, - user_takes_after: u32, -) -> (Address, TaprootSpendInfo) { - let deposit_script = - script_builder::create_deposit_script(nofn_xonly_pk, user_evm_address, amount); - - let recovery_script_pubkey = recovery_taproot_address - .clone() - .assume_checked() - .script_pubkey(); - let recovery_extracted_xonly_pk = - XOnlyPublicKey::from_slice(&recovery_script_pubkey.as_bytes()[2..34]).unwrap(); - - let script_timelock = script_builder::generate_relative_timelock_script( - recovery_extracted_xonly_pk, - user_takes_after, - ); - - create_taproot_address(&[deposit_script, script_timelock], None, network) -} - -/// Shorthand function for creating a MuSig2 taproot address: No scripts and -/// `nofn_xonly_pk` as the internal key. -/// -/// # Returns -/// -/// See [`create_taproot_address`]. -/// -/// - [`Address`]: MuSig2 taproot Bitcoin address -/// - [`TaprootSpendInfo`]: MuSig2 address's taproot spending information -pub fn create_musig2_address( - nofn_xonly_pk: XOnlyPublicKey, - network: bitcoin::Network, -) -> (Address, TaprootSpendInfo) { - create_taproot_address(&[], Some(nofn_xonly_pk), network) -} - -/// Creates a kickoff taproot address with multisig script. -/// -/// # Returns -/// -/// See [`create_taproot_address`]. -/// -/// - [`Address`]: Kickoff taproot Bitcoin address -/// - [`TaprootSpendInfo`]: Kickoff address's taproot spending information -pub fn create_kickoff_address( - nofn_xonly_pk: XOnlyPublicKey, - operator_xonly_pk: XOnlyPublicKey, - network: bitcoin::Network, -) -> (Address, TaprootSpendInfo) { - let musig2_and_operator_script = script_builder::create_musig2_and_operator_multisig_script( - nofn_xonly_pk, - operator_xonly_pk, - ); - - create_taproot_address(&[musig2_and_operator_script], None, network) -} - // Transaction Builders -------------------------------------------------------- /// Creates the move_tx to move the deposit. @@ -181,11 +34,11 @@ pub fn create_move_tx( bridge_amount_sats: u64, network: bitcoin::Network, ) -> Transaction { - let (musig2_address, _) = create_musig2_address(nofn_xonly_pk, network); + let (musig2_address, _) = builder::address::create_musig2_address(nofn_xonly_pk, network); let tx_ins = create_tx_ins(vec![deposit_outpoint]); - let anyone_can_spend_txout = script_builder::anyone_can_spend_txout(); + let anyone_can_spend_txout = builder::script::anyone_can_spend_txout(); let move_txout = TxOut { value: Amount::from_sat(bridge_amount_sats) - Amount::from_sat(MOVE_TX_MIN_RELAY_FEE) @@ -208,7 +61,7 @@ pub fn create_move_tx_handler( ) -> TxHandler { let move_tx = create_move_tx(deposit_outpoint, nofn_xonly_pk, bridge_amount_sats, network); - let (deposit_address, deposit_taproot_spend_info) = generate_deposit_address( + let (deposit_address, deposit_taproot_spend_info) = builder::address::generate_deposit_address( nofn_xonly_pk, recovery_taproot_address, evm_address, @@ -222,7 +75,7 @@ pub fn create_move_tx_handler( value: Amount::from_sat(bridge_amount_sats), }]; - let deposit_script = vec![script_builder::create_deposit_script( + let deposit_script = vec![builder::script::create_deposit_script( nofn_xonly_pk, evm_address, bridge_amount_sats, @@ -258,16 +111,16 @@ pub fn create_kickoff_utxo_tx( // = 154 + 43 * num_kickoff_utxos_per_tx; let tx_ins = create_tx_ins(vec![funding_utxo.outpoint]); - let musig2_and_operator_script = script_builder::create_musig2_and_operator_multisig_script( + let musig2_and_operator_script = builder::script::create_musig2_and_operator_multisig_script( nofn_xonly_pk, operator_xonly_pk, ); let (musig2_and_operator_address, _) = - create_taproot_address(&[musig2_and_operator_script], None, network); + builder::address::create_taproot_address(&[musig2_and_operator_script], None, network); let operator_address = Address::p2tr(&utils::SECP, operator_xonly_pk, None, network); let change_amount = funding_utxo.txout.value - Amount::from_sat(KICKOFF_UTXO_AMOUNT_SATS * num_kickoff_utxos_per_tx as u64) - - script_builder::anyone_can_spend_txout().value + - builder::script::anyone_can_spend_txout().value - Amount::from_sat(kickoff_tx_min_relay_fee as u64); tracing::debug!("Change amount: {:?}", change_amount); let mut tx_outs_raw = vec![ @@ -280,8 +133,8 @@ pub fn create_kickoff_utxo_tx( tx_outs_raw.push((change_amount, operator_address.script_pubkey())); tx_outs_raw.push(( - script_builder::anyone_can_spend_txout().value, - script_builder::anyone_can_spend_txout().script_pubkey, + builder::script::anyone_can_spend_txout().value, + builder::script::anyone_can_spend_txout().script_pubkey, )); let tx_outs = create_tx_outs(tx_outs_raw); let tx = create_btc_tx(tx_ins, tx_outs); @@ -312,14 +165,14 @@ pub fn create_slash_or_take_tx( let move_txid = move_tx.compute_txid(); let (kickoff_utxo_address, kickoff_utxo_spend_info) = - create_kickoff_address(nofn_xonly_pk, operator_xonly_pk, network); + builder::address::create_kickoff_address(nofn_xonly_pk, operator_xonly_pk, network); // tracing::debug!( // "kickoff_utxo_script_pubkey: {:?}", // kickoff_utxo_address.script_pubkey() // ); // tracing::debug!("kickoff_utxo_spend_info: {:?}", kickoff_utxo_spend_info); // tracing::debug!("kickoff_utxooo: {:?}", kickoff_utxo); - let musig2_and_operator_script = script_builder::create_musig2_and_operator_multisig_script( + let musig2_and_operator_script = builder::script::create_musig2_and_operator_multisig_script( nofn_xonly_pk, operator_xonly_pk, ); @@ -338,8 +191,8 @@ pub fn create_slash_or_take_tx( assert!(kickoff_utxo_address.script_pubkey() == kickoff_utxo.txout.script_pubkey); let ins = create_tx_ins(vec![kickoff_utxo.outpoint]); let relative_timelock_script = - script_builder::generate_relative_timelock_script(operator_xonly_pk, operator_takes_after); - let (slash_or_take_address, _) = create_taproot_address( + builder::script::generate_relative_timelock_script(operator_xonly_pk, operator_takes_after); + let (slash_or_take_address, _) = builder::address::create_taproot_address( &[relative_timelock_script.clone()], Some(nofn_xonly_pk), network, @@ -348,7 +201,7 @@ pub fn create_slash_or_take_tx( op_return_script.extend(utils::usize_to_var_len_bytes(operator_idx)); let mut push_bytes = PushBytesBuf::new(); push_bytes.extend_from_slice(&op_return_script).unwrap(); - let op_return_txout = script_builder::op_return_txout(push_bytes); + let op_return_txout = builder::script::op_return_txout(push_bytes); let outs = vec![ TxOut { value: Amount::from_sat( @@ -356,7 +209,7 @@ pub fn create_slash_or_take_tx( ), script_pubkey: slash_or_take_address.script_pubkey(), }, - script_builder::anyone_can_spend_txout(), + builder::script::anyone_can_spend_txout(), op_return_txout, ]; let tx = create_btc_tx(ins, outs); @@ -388,15 +241,17 @@ pub fn create_operator_takes_tx( operator_takes_after as u16, )); - let (musig2_address, musig2_spend_info) = create_musig2_address(nofn_xonly_pk, network); + let (musig2_address, musig2_spend_info) = + builder::address::create_musig2_address(nofn_xonly_pk, network); let relative_timelock_script = - script_builder::generate_relative_timelock_script(operator_xonly_pk, operator_takes_after); - let (slash_or_take_address, slash_or_take_spend_info) = create_taproot_address( - &[relative_timelock_script.clone()], - Some(nofn_xonly_pk), - network, - ); + builder::script::generate_relative_timelock_script(operator_xonly_pk, operator_takes_after); + let (slash_or_take_address, slash_or_take_spend_info) = + builder::address::create_taproot_address( + &[relative_timelock_script.clone()], + Some(nofn_xonly_pk), + network, + ); // Sanity check TODO: No asserts outside of tests assert!(slash_or_take_address.script_pubkey() == slash_or_take_utxo.txout.script_pubkey); @@ -407,11 +262,11 @@ pub fn create_operator_takes_tx( + Amount::from_sat(bridge_amount_sats) - Amount::from_sat(MOVE_TX_MIN_RELAY_FEE) - Amount::from_sat(OPERATOR_TAKES_TX_MIN_RELAY_FEE) - - script_builder::anyone_can_spend_txout().value - - script_builder::anyone_can_spend_txout().value, + - builder::script::anyone_can_spend_txout().value + - builder::script::anyone_can_spend_txout().value, script_pubkey: operator_wallet_address_checked.script_pubkey(), }, - script_builder::anyone_can_spend_txout(), + builder::script::anyone_can_spend_txout(), ]; let tx = create_btc_tx(ins, outs); let prevouts = vec![ @@ -419,7 +274,7 @@ pub fn create_operator_takes_tx( script_pubkey: musig2_address.script_pubkey(), value: Amount::from_sat(bridge_amount_sats) - Amount::from_sat(MOVE_TX_MIN_RELAY_FEE) - - script_builder::anyone_can_spend_txout().value, + - builder::script::anyone_can_spend_txout().value, }, slash_or_take_utxo.txout, ]; @@ -488,9 +343,8 @@ pub fn create_tx_outs(pairs: Vec<(Amount, ScriptBuf)>) -> Vec { #[cfg(test)] mod tests { use crate::{ + builder, musig2::AggregateFromPublicKeys, - script_builder, - transaction_builder::{self, create_musig2_address}, utils::{self, SECP}, }; use bitcoin::{ @@ -508,7 +362,7 @@ mod tests { // No internal key or scripts (key path spend). let (address, spend_info) = - super::create_taproot_address(&[], None, bitcoin::Network::Regtest); + builder::address::create_taproot_address(&[], None, bitcoin::Network::Regtest); assert_eq!(address.address_type().unwrap(), AddressType::P2tr); assert!(address.is_related_to_xonly_pubkey( &utils::UNSPENDABLE_XONLY_PUBKEY @@ -520,8 +374,11 @@ mod tests { assert!(spend_info.merkle_root().is_none()); // Key path spend. - let (address, spend_info) = - super::create_taproot_address(&[], Some(internal_key), bitcoin::Network::Regtest); + let (address, spend_info) = builder::address::create_taproot_address( + &[], + Some(internal_key), + bitcoin::Network::Regtest, + ); assert_eq!(address.address_type().unwrap(), AddressType::P2tr); assert!(address.is_related_to_xonly_pubkey( &internal_key @@ -533,8 +390,11 @@ mod tests { assert!(spend_info.merkle_root().is_none()); let scripts = [ScriptBuf::new()]; - let (address, spend_info) = - super::create_taproot_address(&scripts, Some(internal_key), bitcoin::Network::Regtest); + let (address, spend_info) = builder::address::create_taproot_address( + &scripts, + Some(internal_key), + bitcoin::Network::Regtest, + ); assert_eq!(address.address_type().unwrap(), AddressType::P2tr); assert!(address.is_related_to_xonly_pubkey( &internal_key @@ -546,8 +406,11 @@ mod tests { assert!(spend_info.merkle_root().is_some()); let scripts = [ScriptBuf::new(), ScriptBuf::new()]; - let (address, spend_info) = - super::create_taproot_address(&scripts, Some(internal_key), bitcoin::Network::Regtest); + let (address, spend_info) = builder::address::create_taproot_address( + &scripts, + Some(internal_key), + bitcoin::Network::Regtest, + ); assert_eq!(address.address_type().unwrap(), AddressType::P2tr); assert!(address.is_related_to_xonly_pubkey( &internal_key @@ -585,7 +448,7 @@ mod tests { Address::from_str("bcrt1p65yp9q9fxtf7dyvthyrx26xxm2czanvrnh9rtvphmlsjvhdt4k6qw4pkss") .unwrap(); - let deposit_address = transaction_builder::generate_deposit_address( + let deposit_address = builder::address::generate_deposit_address( nofn_xonly_pk, recovery_taproot_address.as_unchecked(), crate::EVMAddress(evm_address), @@ -622,13 +485,13 @@ mod tests { ); assert_eq!( move_tx.output.first().unwrap().script_pubkey, - create_musig2_address(nofn_xonly_pk, network) + builder::address::create_musig2_address(nofn_xonly_pk, network) .0 .script_pubkey() ); assert_eq!( *move_tx.output.get(1).unwrap(), - script_builder::anyone_can_spend_txout() + builder::script::anyone_can_spend_txout() ); } } diff --git a/core/src/extended_rpc.rs b/core/src/extended_rpc.rs index e97775869..9baf2c4d4 100644 --- a/core/src/extended_rpc.rs +++ b/core/src/extended_rpc.rs @@ -2,8 +2,8 @@ //! //! This module provides helpful functions for Bitcoin RPC. +use crate::builder; use crate::errors::BridgeError; -use crate::transaction_builder; use crate::EVMAddress; use bitcoin::address::NetworkUnchecked; use bitcoin::Address; @@ -177,7 +177,7 @@ where return Err(BridgeError::DepositNotFinalized); } - let (deposit_address, _) = transaction_builder::generate_deposit_address( + let (deposit_address, _) = builder::address::generate_deposit_address( nofn_xonly_pk, recovery_taproot_address, evm_address, diff --git a/core/src/lib.rs b/core/src/lib.rs index 245961cdb..0c9a03168 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; pub mod actor; pub mod aggregator; +pub mod builder; pub mod cli; pub mod config; pub mod constants; @@ -23,10 +24,8 @@ pub mod merkle; pub mod mock; pub mod musig2; pub mod operator; -pub mod script_builder; pub mod servers; pub mod traits; -pub mod transaction_builder; pub mod user; pub mod utils; pub mod verifier; diff --git a/core/src/musig2.rs b/core/src/musig2.rs index 3fa198bb5..4ee9b7753 100644 --- a/core/src/musig2.rs +++ b/core/src/musig2.rs @@ -174,9 +174,9 @@ mod tests { use super::{nonce_pair, MuSigNoncePair}; use crate::{ actor::Actor, + builder::{self, transaction::TxHandler}, errors::BridgeError, musig2::{AggregateFromPublicKeys, MuSigPartialSignature}, - transaction_builder::{self, TxHandler}, utils, ByteArray32, }; use bitcoin::{ @@ -435,7 +435,7 @@ mod tests { bitcoin::Network::Regtest, ); let (sending_address, sending_address_spend_info) = - transaction_builder::create_taproot_address( + builder::address::create_taproot_address( &scripts.clone(), Some(untweaked_xonly_pubkey), bitcoin::Network::Regtest, @@ -448,12 +448,12 @@ mod tests { txid: Txid::from_byte_array([0u8; 32]), vout: 0, }; - let tx_outs = transaction_builder::create_tx_outs(vec![( + let tx_outs = builder::transaction::create_tx_outs(vec![( Amount::from_sat(99_000_000), receiving_address.script_pubkey(), )]); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); - let dummy_tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); + let dummy_tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut tx_details = TxHandler { tx: dummy_tx, prevouts: vec![prevout], @@ -525,7 +525,7 @@ mod tests { bitcoin::Network::Regtest, ); let (sending_address, sending_address_spend_info) = - transaction_builder::create_taproot_address( + builder::address::create_taproot_address( &scripts.clone(), None, bitcoin::Network::Regtest, @@ -538,12 +538,12 @@ mod tests { txid: Txid::from_byte_array([0u8; 32]), vout: 0, }; - let tx_outs = transaction_builder::create_tx_outs(vec![( + let tx_outs = builder::transaction::create_tx_outs(vec![( Amount::from_sat(99_000_000), receiving_address.script_pubkey(), )]); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); - let dummy_tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); + let dummy_tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut tx_details = TxHandler { tx: dummy_tx, prevouts: vec![prevout], diff --git a/core/src/operator.rs b/core/src/operator.rs index 7df119e2a..f6b5555a5 100644 --- a/core/src/operator.rs +++ b/core/src/operator.rs @@ -1,13 +1,14 @@ use crate::actor::Actor; +use crate::builder::transaction::KICKOFF_UTXO_AMOUNT_SATS; +use crate::builder::{self}; use crate::config::BridgeConfig; use crate::database::operator::OperatorDB; use crate::errors::BridgeError; use crate::extended_rpc::ExtendedRpc; use crate::musig2::AggregateFromPublicKeys; use crate::traits::rpc::OperatorRpcServer; -use crate::transaction_builder::{self, KICKOFF_UTXO_AMOUNT_SATS}; use crate::utils::handle_taproot_witness_new; -use crate::{script_builder, utils, EVMAddress, UTXO}; +use crate::{utils, EVMAddress, UTXO}; use bitcoin::address::NetworkUnchecked; use bitcoin::consensus::deserialize; use bitcoin::hashes::Hash; @@ -233,7 +234,7 @@ where self.signer.address.clone(), )); } - let mut kickoff_tx_handler = transaction_builder::create_kickoff_utxo_tx( + let mut kickoff_tx_handler = builder::transaction::create_kickoff_utxo_tx( &funding_utxo, self.nofn_xonly_pk, self.signer.xonly_public_key, @@ -408,9 +409,9 @@ where &input_utxo.txout.script_pubkey.as_bytes()[2..34], )?; - let tx_ins = transaction_builder::create_tx_ins(vec![input_utxo.outpoint]); + let tx_ins = builder::transaction::create_tx_ins(vec![input_utxo.outpoint]); let tx_outs = vec![output_txout.clone()]; - let mut tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let mut tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut sighash_cache = SighashCache::new(&tx); let sighash = sighash_cache.taproot_key_spend_signature_hash( @@ -435,7 +436,7 @@ where push_bytes .extend_from_slice(&utils::usize_to_var_len_bytes(self.idx)) .unwrap(); - let op_return_txout = script_builder::op_return_txout(push_bytes); + let op_return_txout = builder::script::op_return_txout(push_bytes); tx.output.push(op_return_txout.clone()); @@ -501,7 +502,7 @@ where } // Calculate move_txid. - let move_tx = transaction_builder::create_move_tx( + let move_tx = builder::transaction::create_move_tx( deposit_outpoint, self.nofn_xonly_pk, self.config.bridge_amount_sats, @@ -567,7 +568,7 @@ where return Err(BridgeError::KickoffGeneratorTxsTooManyIterations); // TODO: Fix this error } - let mut slash_or_take_tx_handler = transaction_builder::create_slash_or_take_tx( + let mut slash_or_take_tx_handler = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo.clone(), self.signer.xonly_public_key, @@ -606,7 +607,7 @@ where txs_to_be_sent.push(slash_or_take_tx_handler.tx.raw_hex()); - let move_tx = transaction_builder::create_move_tx( + let move_tx = builder::transaction::create_move_tx( deposit_outpoint, self.nofn_xonly_pk, self.config.bridge_amount_sats, @@ -617,7 +618,7 @@ where vout: 0, }; - let mut operator_takes_tx = transaction_builder::create_operator_takes_tx( + let mut operator_takes_tx = builder::transaction::create_operator_takes_tx( bridge_fund_outpoint, slash_or_take_utxo, self.signer.xonly_public_key, diff --git a/core/src/user.rs b/core/src/user.rs index 68cad5d55..8882053c6 100644 --- a/core/src/user.rs +++ b/core/src/user.rs @@ -1,9 +1,9 @@ use crate::actor::Actor; +use crate::builder; use crate::config::BridgeConfig; use crate::errors::BridgeError; use crate::extended_rpc::ExtendedRpc; use crate::musig2::AggregateFromPublicKeys; -use crate::transaction_builder; use crate::{EVMAddress, UTXO}; use bitcoin::{Address, TxOut}; use bitcoin::{Amount, OutPoint}; @@ -57,7 +57,7 @@ where #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] pub fn get_deposit_address(&self, evm_address: EVMAddress) -> Result { - let (deposit_address, _) = transaction_builder::generate_deposit_address( + let (deposit_address, _) = builder::address::generate_deposit_address( self.nofn_xonly_pk, self.signer.address.as_unchecked(), evm_address, @@ -93,14 +93,14 @@ where }, }; - let txins = transaction_builder::create_tx_ins(vec![dust_utxo.outpoint]); + let txins = builder::transaction::create_tx_ins(vec![dust_utxo.outpoint]); let txout = TxOut { value: Amount::from_sat(withdrawal_amount), // TODO: Change this in the future since Operators should profit from the bridge script_pubkey: withdrawal_address.script_pubkey(), }; let txouts = vec![txout.clone()]; - let mut tx = transaction_builder::create_btc_tx(txins, txouts.clone()); + let mut tx = builder::transaction::create_btc_tx(txins, txouts.clone()); let prevouts = vec![dust_utxo.txout.clone()]; let sig = self.signer.sign_taproot_pubkey_spend_tx_with_sighash( diff --git a/core/src/utils.rs b/core/src/utils.rs index 26c59a7c7..7f2aa3745 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -1,7 +1,7 @@ +use crate::builder::transaction::TxHandler; use crate::cli::Args; use crate::config::BridgeConfig; use crate::errors::BridgeError; -use crate::transaction_builder::TxHandler; use bitcoin; use bitcoin::sighash::SighashCache; use bitcoin::taproot::LeafVersion; diff --git a/core/src/verifier.rs b/core/src/verifier.rs index f81f40359..835642870 100644 --- a/core/src/verifier.rs +++ b/core/src/verifier.rs @@ -1,4 +1,6 @@ use crate::actor::Actor; +use crate::builder::transaction::{TxHandler, KICKOFF_UTXO_AMOUNT_SATS}; +use crate::builder::{self}; use crate::config::BridgeConfig; use crate::database::verifier::VerifierDB; use crate::errors::BridgeError; @@ -8,7 +10,6 @@ use crate::musig2::{ MuSigSigHash, }; use crate::traits::rpc::VerifierRpcServer; -use crate::transaction_builder::{self, TxHandler, KICKOFF_UTXO_AMOUNT_SATS}; use crate::{utils, ByteArray32, ByteArray64, ByteArray66, EVMAddress, UTXO}; use bitcoin::address::NetworkUnchecked; use bitcoin::hashes::Hash; @@ -188,7 +189,7 @@ where // Check if for each operator the address of the kickoff_utxo is correct TODO: Maybe handle the possible errors better let (musig2_and_operator_address, spend_info) = - transaction_builder::create_kickoff_address( + builder::address::create_kickoff_address( self.nofn_xonly_pk, self.operator_xonly_pks[i], self.config.network, @@ -203,7 +204,7 @@ where kickoff_utxo.txout.script_pubkey == musig2_and_operator_address.script_pubkey() ); - let mut slash_or_take_tx_handler = transaction_builder::create_slash_or_take_tx( + let mut slash_or_take_tx_handler = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo.clone(), self.config.operators_xonly_pks[i], @@ -217,7 +218,7 @@ where let slash_or_take_tx_sighash = Actor::convert_tx_to_sighash_script_spend(&mut slash_or_take_tx_handler, 0, 0)?; slash_or_take_sighashes.push(ByteArray32(slash_or_take_tx_sighash.to_byte_array())); - // let spend_kickoff_utxo_tx_handler = transaction_builder::create_slash_or_take_tx(deposit_outpoint, kickoff_outpoint, kickoff_txout, operator_address, operator_idx, nofn_xonly_pk, network) + // let spend_kickoff_utxo_tx_handler = builder::transaction::create_slash_or_take_tx(deposit_outpoint, kickoff_outpoint, kickoff_txout, operator_address, operator_idx, nofn_xonly_pk, network) } tracing::debug!( "Slash or take sighashes for verifier: {:?}: {:?}", @@ -294,7 +295,7 @@ where .await? .ok_or(BridgeError::DepositInfoNotFound)?; - let move_tx_handler = transaction_builder::create_move_tx_handler( + let move_tx_handler = builder::transaction::create_move_tx_handler( deposit_outpoint, evm_address, &recovery_taproot_address, @@ -329,7 +330,7 @@ where .iter() .enumerate() .map(|(index, kickoff_utxo)| { - let mut slash_or_take_tx_handler = transaction_builder::create_slash_or_take_tx( + let mut slash_or_take_tx_handler = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo.clone(), self.operator_xonly_pks[index], @@ -360,7 +361,7 @@ where txout: slash_or_take_tx_handler.tx.output[0].clone(), }; - let mut operator_takes_tx = transaction_builder::create_operator_takes_tx( + let mut operator_takes_tx = builder::transaction::create_operator_takes_tx( bridge_fund_outpoint, slash_or_take_utxo, self.operator_xonly_pks[index], @@ -429,7 +430,7 @@ where .iter() .enumerate() .for_each(|(index, kickoff_utxo)| { - let slash_or_take_tx = transaction_builder::create_slash_or_take_tx( + let slash_or_take_tx = builder::transaction::create_slash_or_take_tx( deposit_outpoint, kickoff_utxo.clone(), self.operator_xonly_pks[index], @@ -447,7 +448,7 @@ where }, txout: slash_or_take_tx.tx.output[0].clone(), }; - let mut operator_takes_tx = transaction_builder::create_operator_takes_tx( + let mut operator_takes_tx = builder::transaction::create_operator_takes_tx( bridge_fund_outpoint, slash_or_take_utxo, self.operator_xonly_pks[index], diff --git a/core/tests/citrea b/core/tests/citrea new file mode 160000 index 000000000..f8fa0da9b --- /dev/null +++ b/core/tests/citrea @@ -0,0 +1 @@ +Subproject commit f8fa0da9b024ad43c7d85403d7a224358e78dd79 diff --git a/core/tests/musig2.rs b/core/tests/musig2.rs index 615259092..16b1da0e2 100644 --- a/core/tests/musig2.rs +++ b/core/tests/musig2.rs @@ -1,5 +1,6 @@ use bitcoin::opcodes::all::OP_CHECKSIG; use bitcoin::{hashes::Hash, script, Amount, ScriptBuf}; +use clementine_core::builder::transaction::TxHandler; use clementine_core::create_extended_rpc; use clementine_core::mock::database::create_test_config_with_thread_name; use clementine_core::musig2::{ @@ -9,10 +10,10 @@ use clementine_core::utils::{handle_taproot_witness_new, SECP}; use clementine_core::ByteArray32; use clementine_core::{ actor::Actor, + builder::{self}, config::BridgeConfig, extended_rpc::ExtendedRpc, musig2::{create_key_agg_ctx, nonce_pair, partial_sign, MuSigNoncePair}, - transaction_builder::{self, TxHandler}, utils, ByteArray66, }; use secp256k1::{Keypair, Message, PublicKey}; @@ -72,22 +73,19 @@ async fn key_spend() { get_verifiers_keys(&config); let (nonce_pairs, agg_nonce) = get_nonces(verifiers_secret_public_keys.clone()); - let (to_address, _) = transaction_builder::create_taproot_address(&[], None, config.network); - let (from_address, from_address_spend_info) = transaction_builder::create_taproot_address( - &[], - Some(untweaked_xonly_pubkey), - config.network, - ); + let (to_address, _) = builder::address::create_taproot_address(&[], None, config.network); + let (from_address, from_address_spend_info) = + builder::address::create_taproot_address(&[], Some(untweaked_xonly_pubkey), config.network); let utxo = rpc.send_to_address(&from_address, 100_000_000).unwrap(); let prevout = rpc.get_txout_from_outpoint(&utxo).unwrap(); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); - let tx_outs = transaction_builder::create_tx_outs(vec![( + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); + let tx_outs = builder::transaction::create_tx_outs(vec![( Amount::from_sat(99_000_000), to_address.script_pubkey(), )]); - let dummy_tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let dummy_tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut tx_details = TxHandler { tx: dummy_tx, @@ -158,8 +156,8 @@ async fn key_spend_with_script() { let dummy_script = script::Builder::new().push_int(1).into_script(); let scripts: Vec = vec![dummy_script]; - let (to_address, _) = transaction_builder::create_taproot_address(&[], None, config.network); - let (from_address, from_address_spend_info) = transaction_builder::create_taproot_address( + let (to_address, _) = builder::address::create_taproot_address(&[], None, config.network); + let (from_address, from_address_spend_info) = builder::address::create_taproot_address( &scripts, Some(untweaked_xonly_pubkey), config.network, @@ -167,13 +165,13 @@ async fn key_spend_with_script() { let utxo = rpc.send_to_address(&from_address, 100_000_000).unwrap(); let prevout = rpc.get_txout_from_outpoint(&utxo).unwrap(); - let tx_outs = transaction_builder::create_tx_outs(vec![( + let tx_outs = builder::transaction::create_tx_outs(vec![( Amount::from_sat(99_000_000), to_address.script_pubkey(), )]); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); - let dummy_tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); + let dummy_tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut tx_details = TxHandler { tx: dummy_tx, @@ -262,17 +260,17 @@ async fn script_spend() { bitcoin::Network::Regtest, ); let (from_address, from_address_spend_info) = - transaction_builder::create_taproot_address(&scripts, None, bitcoin::Network::Regtest); + builder::address::create_taproot_address(&scripts, None, bitcoin::Network::Regtest); let utxo = rpc.send_to_address(&from_address, 100_000_000).unwrap(); let prevout = rpc.get_txout_from_outpoint(&utxo).unwrap(); - let tx_outs = transaction_builder::create_tx_outs(vec![( + let tx_outs = builder::transaction::create_tx_outs(vec![( Amount::from_sat(99_000_000), to_address.script_pubkey(), )]); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); - let dummy_tx = transaction_builder::create_btc_tx(tx_ins, tx_outs); + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); + let dummy_tx = builder::transaction::create_btc_tx(tx_ins, tx_outs); let mut tx_details = TxHandler { tx: dummy_tx, prevouts: vec![prevout], diff --git a/core/tests/taproot.rs b/core/tests/taproot.rs index e799de2e8..602b8bf58 100644 --- a/core/tests/taproot.rs +++ b/core/tests/taproot.rs @@ -3,10 +3,11 @@ use bitcoin::opcodes::all::OP_CHECKSIG; use bitcoin::script::Builder; use bitcoin::{Address, Amount, TapTweakHash, TxOut, XOnlyPublicKey}; use clementine_core::actor::Actor; +use clementine_core::builder::transaction::TxHandler; +use clementine_core::builder::{self}; use clementine_core::create_extended_rpc; use clementine_core::extended_rpc::ExtendedRpc; use clementine_core::mock::database::create_test_config_with_thread_name; -use clementine_core::transaction_builder::{self, TxHandler}; use clementine_core::utils::{handle_taproot_witness_new, SECP}; #[tokio::test] @@ -37,11 +38,11 @@ async fn create_address_and_transaction_then_sign_transaction() { .push_opcode(OP_CHECKSIG) .into_script(); let (taproot_address, taproot_spend_info) = - transaction_builder::create_taproot_address(&[to_pay_script.clone()], None, config.network); + builder::address::create_taproot_address(&[to_pay_script.clone()], None, config.network); // Create a new transaction. let utxo = rpc.send_to_address(&taproot_address, 1000).unwrap(); - let tx_ins = transaction_builder::create_tx_ins(vec![utxo]); + let tx_ins = builder::transaction::create_tx_ins(vec![utxo]); let tx_outs = vec![TxOut { value: Amount::from_sat(330), script_pubkey: taproot_address.script_pubkey(), @@ -50,7 +51,7 @@ async fn create_address_and_transaction_then_sign_transaction() { value: Amount::from_sat(1000), script_pubkey: taproot_address.script_pubkey(), }]; - let tx = transaction_builder::create_btc_tx(tx_ins, tx_outs.clone()); + let tx = builder::transaction::create_btc_tx(tx_ins, tx_outs.clone()); let mut tx_details = TxHandler { tx: tx.clone(), prevouts,