diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 487d0577..411d7469 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,10 @@ jobs: stable, nightly ] + test: [ + transactions, + taproot + ] runs-on: ubuntu-latest container: @@ -74,7 +78,7 @@ jobs: profile: minimal - name: Run regtest Bitcoin transactions - run: cargo test --verbose --test transactions --features rpc -- --test-threads=1 + run: cargo test --verbose --test ${{ matrix.test }} --features taproot,rpc -- --test-threads=1 env: RPC_HOST: bitcoind RPC_PORT: 18443 diff --git a/contrib/run-rpc-tests.sh b/contrib/run-rpc-tests.sh index 7e4eb7fb..da6a2725 100755 --- a/contrib/run-rpc-tests.sh +++ b/contrib/run-rpc-tests.sh @@ -25,7 +25,13 @@ do done export CI=false RPC_USER=test RPC_PASS=cEl2o3tHHgzYeuu3CiiZ2FjdgSiw9wNeMFzoNbFmx9k= -cargo test --test transactions --features rpc -- --test-threads=1 + +case "$1" in + "--taproot") + cargo test --test taproot --features taproot,rpc -- --test-threads=1;; + *) + cargo test --test transactions --features rpc -- --test-threads=1;; +esac docker kill bitcoind diff --git a/src/bitcoin/taproot/funding.rs b/src/bitcoin/taproot/funding.rs new file mode 100644 index 00000000..d0721ebf --- /dev/null +++ b/src/bitcoin/taproot/funding.rs @@ -0,0 +1,96 @@ +use bitcoin::blockdata::transaction::{OutPoint, Transaction}; +use bitcoin::secp256k1::Secp256k1; +use bitcoin::{Address, XOnlyPublicKey}; + +use crate::blockchain::Network; +use crate::transaction::{Error, Fundable, Linkable}; + +use crate::bitcoin::taproot::Taproot; +use crate::bitcoin::transaction::MetadataOutput; +use crate::bitcoin::Bitcoin; +use bitcoin::util::taproot::TaprootSpendInfo; + +#[derive(Debug, Clone)] +pub struct Funding { + untweaked_public_key: Option, + network: Option, + seen_tx: Option, +} + +impl Linkable for Funding { + fn get_consumable_output(&self) -> Result { + let secp = Secp256k1::new(); + + let address = Address::p2tr( + &secp, + self.untweaked_public_key.ok_or(Error::MissingPublicKey)?, + None, + self.network.ok_or(Error::MissingNetwork)?, + ); + let script_pubkey = address.script_pubkey(); + + match &self.seen_tx { + Some(t) => t + .output + .iter() + .enumerate() + .find(|(_, tx_out)| tx_out.script_pubkey == script_pubkey) + .map(|(ix, tx_out)| MetadataOutput { + out_point: OutPoint::new(t.txid(), ix as u32), + tx_out: tx_out.clone(), + script_pubkey: Some(script_pubkey), + }) + .ok_or(Error::MissingUTXO), + // The transaction has not been see yet, cannot infer the UTXO + None => Err(Error::MissingOnchainTransaction), + } + } +} + +impl Fundable, MetadataOutput> for Funding { + fn initialize(pubkey: XOnlyPublicKey, network: Network) -> Result { + let network = match network { + Network::Mainnet => bitcoin::Network::Bitcoin, + Network::Testnet => bitcoin::Network::Testnet, + Network::Local => bitcoin::Network::Regtest, + }; + Ok(Funding { + untweaked_public_key: Some(pubkey), + network: Some(network), + seen_tx: None, + }) + } + + fn get_address(&self) -> Result { + let secp = Secp256k1::new(); + let taproot_info = TaprootSpendInfo::new_key_spend( + &secp, + self.untweaked_public_key.ok_or(Error::MissingPublicKey)?, + None, + ); + + Ok(Address::p2tr( + &secp, + taproot_info.internal_key(), + taproot_info.merkle_root(), + self.network.ok_or(Error::MissingNetwork)?, + )) + } + + fn update(&mut self, tx: Transaction) -> Result<(), Error> { + self.seen_tx = Some(tx); + Ok(()) + } + + fn raw(tx: Transaction) -> Result { + Ok(Self { + untweaked_public_key: None, + network: None, + seen_tx: Some(tx), + }) + } + + fn was_seen(&self) -> bool { + self.seen_tx.is_some() + } +} diff --git a/src/bitcoin/taproot/lock.rs b/src/bitcoin/taproot/lock.rs new file mode 100644 index 00000000..031e3016 --- /dev/null +++ b/src/bitcoin/taproot/lock.rs @@ -0,0 +1,125 @@ +use std::marker::PhantomData; + +use bitcoin::blockdata::opcodes::all::{OP_CHECKSIG, OP_CHECKSIGADD, OP_EQUAL}; +use bitcoin::blockdata::script::Builder; +use bitcoin::blockdata::transaction::{TxIn, TxOut}; +use bitcoin::blockdata::witness::Witness; +use bitcoin::secp256k1::rand::thread_rng; +use bitcoin::secp256k1::Secp256k1; +use bitcoin::util::psbt::PartiallySignedTransaction; +use bitcoin::Script; +use bitcoin::{Amount, KeyPair, XOnlyPublicKey}; + +use crate::script; +use crate::transaction::{Error as FError, Fundable, Lockable}; + +use crate::bitcoin::taproot::Taproot; +use crate::bitcoin::timelock::CSVTimelock; +use crate::bitcoin::transaction::{Error, MetadataOutput, SubTransaction, Tx}; +use crate::bitcoin::Bitcoin; +use bitcoin::util::taproot::TaprootBuilder; + +#[derive(Debug)] +pub struct Lock; + +impl SubTransaction for Lock { + fn finalize(psbt: &mut PartiallySignedTransaction) -> Result<(), FError> { + //let (pubkey, full_sig) = psbt.inputs[0] + // .partial_sigs + // .iter() + // .next() + // .ok_or(FError::MissingSignature)?; + //psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![ + // full_sig.to_vec(), + // pubkey.serialize().to_vec(), + //])); + let sig = psbt.inputs[0].tap_key_sig.ok_or(FError::MissingSignature)?; + psbt.inputs[0].final_script_witness = Some(Witness::from_vec(vec![sig.to_vec()])); + Ok(()) + } +} + +impl Lockable, MetadataOutput> for Tx { + fn initialize( + prev: &impl Fundable, MetadataOutput>, + lock: script::DataLock>, + target_amount: Amount, + ) -> Result { + let secp = Secp256k1::new(); + // FIXME: for a no key spend taproot tx choose a non-spendable internal key and not a + // random one as follow + let untweaked_public_key = + XOnlyPublicKey::from_keypair(&KeyPair::new(&secp, &mut thread_rng())); + let spend_info = TaprootBuilder::new() + // Buy script + .add_leaf( + 1, + Builder::new() + .push_slice(lock.success.alice.serialize().as_ref()) + .push_opcode(OP_CHECKSIG) + .push_slice(lock.success.bob.serialize().as_ref()) + .push_opcode(OP_CHECKSIGADD) + .push_int(2) + .push_opcode(OP_EQUAL) + .into_script(), + ) + // FIXME + .unwrap() + // Cancel script + .add_leaf( + 1, + Builder::new() + .push_slice(lock.failure.alice.serialize().as_ref()) + .push_opcode(OP_CHECKSIG) + .push_slice(lock.failure.bob.serialize().as_ref()) + .push_opcode(OP_CHECKSIGADD) + .push_int(1) // FIXME this is just for making different script (same keys for now between success and failure) + .push_opcode(OP_EQUAL) + .into_script(), + ) + // FIXME + .unwrap() + .finalize(&secp, untweaked_public_key) + .expect("Valid taproot FIXME"); + println!("{:#?}", spend_info); + let tweaked_pubkey = spend_info.output_key(); + let output_metadata = prev.get_consumable_output()?; + + if output_metadata.tx_out.value < target_amount.as_sat() { + return Err(FError::NotEnoughAssets); + } + + let unsigned_tx = bitcoin::Transaction { + version: 2, + lock_time: 0, + input: vec![TxIn { + previous_output: output_metadata.out_point, + script_sig: Script::default(), + sequence: CSVTimelock::disable(), + witness: Witness::new(), + }], + output: vec![TxOut { + value: target_amount.as_sat(), + script_pubkey: Script::new_v1_p2tr_tweaked(tweaked_pubkey), + }], + }; + + let mut psbt = + PartiallySignedTransaction::from_unsigned_tx(unsigned_tx).map_err(Error::from)?; + + // Set the input witness data and sighash type + psbt.inputs[0].witness_utxo = Some(output_metadata.tx_out); + psbt.inputs[0].witness_script = output_metadata.script_pubkey; + + // FIXME: add tap scripts in PSBT + + Ok(Tx { + psbt, + _t: PhantomData, + }) + } + + fn verify_template(&self, _lock: script::DataLock>) -> Result<(), FError> { + todo!() + } +} diff --git a/src/bitcoin/taproot/mod.rs b/src/bitcoin/taproot/mod.rs index f9520007..8c0dc759 100644 --- a/src/bitcoin/taproot/mod.rs +++ b/src/bitcoin/taproot/mod.rs @@ -5,13 +5,27 @@ use std::convert::{TryFrom, TryInto}; use std::fmt; use std::str::FromStr; +use crate::bitcoin::taproot::{funding::Funding, lock::Lock}; + +use crate::bitcoin::transaction::Tx; use crate::bitcoin::{Bitcoin, BitcoinTaproot, Btc, Strategy}; use crate::consensus::{self, CanonicalBytes}; use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys, Signatures}; +use bitcoin::util::taproot::TapSighashHash; //use crate::role::Arbitrating; -use bitcoin::hashes::sha256d::Hash as Sha256dHash; -use bitcoin::secp256k1::{constants::SECRET_KEY_SIZE, schnorr::Signature, KeyPair, XOnlyPublicKey}; +use bitcoin::secp256k1::{ + constants::SECRET_KEY_SIZE, schnorr::Signature, KeyPair, Message, Secp256k1, XOnlyPublicKey, +}; + +pub mod funding; +pub mod lock; + +/// Funding the swap creating a Taproot (SegWit v1) output. +pub type FundingTx = Funding; + +/// Locking the funding UTXO in a lock and allow buy or cancel transaction. +pub type LockTx = Tx; /// Inner type for the Taproot strategy with on-chain scripts. #[derive(Clone, Debug, Copy, Eq, PartialEq)] @@ -119,7 +133,7 @@ impl SharedSecretKeys for Bitcoin { } impl Signatures for Bitcoin { - type Message = Sha256dHash; + type Message = TapSighashHash; type Signature = Signature; type EncryptedSignature = Signature; } @@ -136,3 +150,13 @@ impl CanonicalBytes for Signature { Signature::from_slice(bytes).map_err(consensus::Error::new) } } + +/// Create a Schnorr signature for the given Taproot sighash +pub fn sign_hash( + sighash: TapSighashHash, + keypair: &bitcoin::secp256k1::KeyPair, +) -> Result { + let context = Secp256k1::new(); + let msg = Message::from_slice(&sighash[..])?; + Ok(context.sign_schnorr(&msg, keypair)) +} diff --git a/src/bitcoin/transaction.rs b/src/bitcoin/transaction.rs index 894bf840..0d39c835 100644 --- a/src/bitcoin/transaction.rs +++ b/src/bitcoin/transaction.rs @@ -8,13 +8,20 @@ use bitcoin::blockdata::transaction::{EcdsaSighashType, OutPoint, TxIn, TxOut}; use bitcoin::util::address; use bitcoin::util::ecdsa::EcdsaSig; use bitcoin::util::psbt::{self, PartiallySignedTransaction}; +use bitcoin::util::sighash::SighashCache; +use bitcoin::util::taproot::TapSighashHash; #[cfg(feature = "experimental")] use bitcoin::{ hashes::sha256d::Hash, - secp256k1::{ecdsa::Signature, PublicKey}, + secp256k1::{ecdsa, PublicKey}, Amount, }; +#[cfg(all(feature = "experimental", feature = "taproot"))] +use bitcoin::{ + secp256k1::schnorr, util::schnorr::SchnorrSig, util::sighash::Prevouts, + util::sighash::SchnorrSighashType, XOnlyPublicKey, +}; use thiserror::Error; @@ -22,6 +29,8 @@ use crate::bitcoin::{Bitcoin, Strategy}; use crate::consensus::{self, CanonicalBytes}; use crate::transaction::{Broadcastable, Error as FError, Finalizable, Linkable}; +#[cfg(all(feature = "experimental", feature = "taproot"))] +use crate::bitcoin::taproot::Taproot; #[cfg(feature = "experimental")] use crate::{ bitcoin::segwitv0::{signature_hash, SegwitV0}, @@ -36,9 +45,9 @@ pub enum Error { /// Multi-input transaction is not supported #[error("Multi-input transaction is not supported")] MultiUTXOUnsuported, - /// SigHash type is missing - #[error("SigHash type is missing")] - MissingSigHashType, + /// Sighash type is missing + #[error("Sighash type is missing")] + MissingSighashType, /// Partially signed transaction error #[error("Partially signed transaction error: `{0}`")] Psbt(#[from] psbt::Error), @@ -122,6 +131,44 @@ where } } +#[cfg(all(feature = "experimental", feature = "taproot"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "experimental", feature = "taproot"))))] +impl Transaction, MetadataOutput> for Tx +where + T: SubTransaction, +{ + fn as_partial(&self) -> &PartiallySignedTransaction { + &self.psbt + } + + fn as_partial_mut(&mut self) -> &mut PartiallySignedTransaction { + &mut self.psbt + } + + fn to_partial(self) -> PartiallySignedTransaction { + self.psbt + } + + fn from_partial(partial: PartiallySignedTransaction) -> Self { + Self { + psbt: partial, + _t: PhantomData, + } + } + + fn based_on(&self) -> MetadataOutput { + MetadataOutput { + out_point: self.psbt.unsigned_tx.input[0].previous_output, + tx_out: self.psbt.inputs[0].witness_utxo.clone().unwrap(), // FIXME + script_pubkey: self.psbt.inputs[0].witness_script.clone(), + } + } + + fn output_amount(&self) -> Amount { + Amount::from_sat(self.psbt.unsigned_tx.output[0].value) + } +} + impl Finalizable for Tx where T: SubTransaction, @@ -191,7 +238,7 @@ where Ok(signature_hash(txin, &script, value, EcdsaSighashType::All)) } - fn add_witness(&mut self, pubkey: PublicKey, sig: Signature) -> Result<(), FError> { + fn add_witness(&mut self, pubkey: PublicKey, sig: ecdsa::Signature) -> Result<(), FError> { let sig_all = EcdsaSig::sighash_all(sig); self.psbt.inputs[0] .partial_sigs @@ -200,6 +247,50 @@ where } } +#[cfg(all(feature = "experimental", feature = "taproot"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "experimental", feature = "taproot"))))] +impl Witnessable> for Tx +where + T: SubTransaction, +{ + // FIXME: this only accounts for key spend and not for script spend + fn generate_witness_message(&self, _path: ScriptPath) -> Result { + let mut sighash = SighashCache::new(&self.psbt.unsigned_tx); + + let witness_utxo = self.psbt.inputs[0] + .witness_utxo + .clone() + .ok_or(FError::MissingWitness)?; + let script_pubkey = self.psbt.inputs[0] + .witness_script + .clone() + .ok_or(FError::MissingWitness)?; + let value = witness_utxo.value; + + let txouts = vec![TxOut { + value, + script_pubkey, + }]; + sighash + .taproot_key_spend_signature_hash(0, &Prevouts::All(&txouts), SchnorrSighashType::All) + .map_err(FError::new) + } + + // FIXME: this only accounts for key spend and not for script spend + fn add_witness( + &mut self, + _pubkey: XOnlyPublicKey, + sig: schnorr::Signature, + ) -> Result<(), FError> { + let sig_all = SchnorrSig { + sig, + hash_ty: SchnorrSighashType::All, + }; + self.psbt.inputs[0].tap_key_sig = Some(sig_all); + Ok(()) + } +} + /// A borrowed reference to a transaction input. #[derive(Debug, Copy, Clone)] pub struct TxInRef<'a> { diff --git a/tests/protocol.rs b/tests/protocol.rs index e5dca3ea..4694d8b4 100644 --- a/tests/protocol.rs +++ b/tests/protocol.rs @@ -193,8 +193,9 @@ fn execute_offline_protocol() { .sign_arbitrating_lock(&mut bob_key_manager, &core) .unwrap(); - let mut lock = LockTx::from_partial(core.lock.clone()); - lock.add_witness(funding_key, signed_lock.lock_sig).unwrap(); + let mut lock = >::from_partial(core.lock.clone()); + Witnessable::::add_witness(&mut lock, funding_key, signed_lock.lock_sig) + .unwrap(); let _ = Broadcastable::::finalize_and_extract(&mut lock).unwrap(); // ...seen arbitrating lock... @@ -231,10 +232,14 @@ fn execute_offline_protocol() { ) .unwrap(); - let mut buy = BuyTx::from_partial(adaptor_buy.buy.clone()); - buy.add_witness(bob_params.buy, fully_sign_buy.buy_adapted_sig) - .unwrap(); - buy.add_witness(alice_params.buy, fully_sign_buy.buy_sig) + let mut buy = >::from_partial(adaptor_buy.buy.clone()); + Witnessable::::add_witness( + &mut buy, + bob_params.buy, + fully_sign_buy.buy_adapted_sig, + ) + .unwrap(); + Witnessable::::add_witness(&mut buy, alice_params.buy, fully_sign_buy.buy_sig) .unwrap(); let buy_tx = Broadcastable::::finalize_and_extract(&mut buy).unwrap(); @@ -268,13 +273,21 @@ fn execute_offline_protocol() { // IF CANCEL PATH: // - let mut cancel = CancelTx::from_partial(core.cancel.clone()); - cancel - .add_witness(bob_params.cancel, bob_cosign_cancel.cancel_sig) - .unwrap(); - cancel - .add_witness(alice_params.cancel, alice_cosign_cancel.cancel_sig) - .unwrap(); + let mut cancel = + >::from_partial(core.cancel.clone()); + + Witnessable::::add_witness( + &mut cancel, + bob_params.cancel, + bob_cosign_cancel.cancel_sig, + ) + .unwrap(); + Witnessable::::add_witness( + &mut cancel, + alice_params.cancel, + alice_cosign_cancel.cancel_sig, + ) + .unwrap(); let _ = Broadcastable::::finalize_and_extract(&mut cancel).unwrap(); // ...seen arbitrating cancel... @@ -287,13 +300,21 @@ fn execute_offline_protocol() { .fully_sign_refund(&mut bob_key_manager, core.clone(), &adaptor_refund) .unwrap(); - let mut refund = RefundTx::from_partial(core.refund.clone()); - refund - .add_witness(alice_params.refund, fully_signed_refund.refund_adapted_sig) - .unwrap(); - refund - .add_witness(bob_params.refund, fully_signed_refund.refund_sig) - .unwrap(); + let mut refund = + >::from_partial(core.refund.clone()); + + Witnessable::::add_witness( + &mut refund, + alice_params.refund, + fully_signed_refund.refund_adapted_sig, + ) + .unwrap(); + Witnessable::::add_witness( + &mut refund, + bob_params.refund, + fully_signed_refund.refund_sig, + ) + .unwrap(); let refund_tx = Broadcastable::::finalize_and_extract(&mut refund).unwrap(); // ...seen refund tx on-chain... @@ -339,9 +360,14 @@ fn execute_offline_protocol() { ) .unwrap(); - let mut punish = PunishTx::from_partial(fully_signed_punish.punish); - punish - .add_witness(alice_params.punish, fully_signed_punish.punish_sig) - .unwrap(); + let mut punish = + >::from_partial(fully_signed_punish.punish); + + Witnessable::::add_witness( + &mut punish, + alice_params.punish, + fully_signed_punish.punish_sig, + ) + .unwrap(); let _ = Broadcastable::::finalize_and_extract(&mut refund).unwrap(); } diff --git a/tests/rpc/mod.rs b/tests/rpc/mod.rs index de70769a..43e7d92d 100644 --- a/tests/rpc/mod.rs +++ b/tests/rpc/mod.rs @@ -71,6 +71,32 @@ macro_rules! new_address { pair.0, ) }}; + (taproot) => {{ + use bitcoin::network::constants::Network; + use bitcoin::secp256k1::rand::thread_rng; + use bitcoin::secp256k1::{KeyPair, Secp256k1}; + use bitcoin::util::address::Address; + use bitcoin::util::taproot::TaprootSpendInfo; + + // Generate random key pair + let s = Secp256k1::new(); + let keypair = KeyPair::new(&s, &mut thread_rng()); + let xpubkey = keypair.public_key().into(); + + let taproot_info = TaprootSpendInfo::new_key_spend(&s, xpubkey, None); + + // Generate pay-to-taproot address + ( + Address::p2tr( + &s, + taproot_info.internal_key(), + taproot_info.merkle_root(), + Network::Regtest, + ), + xpubkey, + keypair, + ) + }}; } macro_rules! send { diff --git a/tests/taproot.rs b/tests/taproot.rs new file mode 100644 index 00000000..ea46b122 --- /dev/null +++ b/tests/taproot.rs @@ -0,0 +1,101 @@ +#![cfg(all(feature = "rpc", feature = "taproot"))] + +use farcaster_core::bitcoin::fee::SatPerVByte; +use farcaster_core::bitcoin::taproot::*; +use farcaster_core::bitcoin::*; +use farcaster_core::blockchain::*; +use farcaster_core::script::*; +use farcaster_core::transaction::*; + +use bitcoin::secp256k1::Secp256k1; +use bitcoin::Amount; +use bitcoincore_rpc::json::AddressType; +use bitcoincore_rpc::RpcApi; + +#[macro_use] +mod rpc; + +#[test] +fn taproot_funding_tx() { + let (_, xpubkey_a1, keypair_a1) = new_address!(taproot); + //let (_, pubkey_a2, secret_a2) = new_address!(); + + let (_, xpubkey_b1, keypair_b1) = new_address!(taproot); + //let (_, pubkey_b2, secret_b2) = new_address!(); + + let mut funding = FundingTx::initialize(xpubkey_a1, Network::Local).unwrap(); + + // + // Create a wallet, mined funds, send to funding address with multiple UTXOs + // + if let Err(_) = rpc::CLIENT.create_wallet("test_wallet", Some(false), None, None, None) { + let wallets = rpc::CLIENT.list_wallets().unwrap(); + if wallets.len() == 0 { + rpc::CLIENT.load_wallet("test_wallet").unwrap(); + } + if wallets.len() > 1 { + panic!("More than one wallet loaded!"); + } + } + + let wallet_address = rpc::CLIENT + .get_new_address(None, Some(AddressType::Bech32)) + .unwrap(); + rpc::CLIENT.generate_to_address(4, &wallet_address).unwrap(); + mine!(100); + let target_swap_amount = bitcoin::Amount::from_btc(8.0).unwrap(); + + let address = funding.get_address().unwrap(); + let txid = rpc::CLIENT + .send_to_address( + &address, + target_swap_amount, + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + + let funding_tx_seen: bitcoin::Transaction = rpc::CLIENT.get_by_id(&txid).unwrap(); + // Minimum of fee of 122 sat + let target_amount = Amount::from_sat(target_swap_amount.as_sat() - 122); + funding.update(funding_tx_seen).unwrap(); + + let datalock = DataLock { + timelock: timelock::CSVTimelock::new(10), + success: DoubleKeys::new(&xpubkey_a1, &xpubkey_b1), + failure: DoubleKeys::new(&xpubkey_a1, &xpubkey_b1), + }; + + let fee = FeeStrategy::Fixed(SatPerVByte::from_sat(1)); + let politic = FeePriority::Low; + + let mut lock = LockTx::initialize(&funding, datalock.clone(), target_amount).unwrap(); + + // + // Sign lock tx + // + let msg = Witnessable::::generate_witness_message(&lock, ScriptPath::Success) + .unwrap(); + // tweak key pair with tap_tweak funding + let secp = Secp256k1::new(); + let tweak = bitcoin::util::taproot::TaprootSpendInfo::new_key_spend(&secp, xpubkey_a1, None) + .tap_tweak(); + let mut tweaked_keypair = keypair_a1.clone(); + tweaked_keypair.tweak_add_assign(&secp, &tweak).unwrap(); + let sig = sign_hash(msg, &tweaked_keypair).unwrap(); + Witnessable::::add_witness(&mut lock, xpubkey_a1, sig).unwrap(); + let lock_finalized = Broadcastable::::finalize_and_extract(&mut lock).unwrap(); + + rpc! { + // Wait 100 blocks to unlock the coinbase + mine 100; + + // Broadcast the lock and mine the transaction + then broadcast lock_finalized; + then mine 1; + } +} diff --git a/tests/transactions.rs b/tests/transactions.rs index 0f8a0709..efb3ce87 100644 --- a/tests/transactions.rs +++ b/tests/transactions.rs @@ -85,7 +85,12 @@ macro_rules! setup_txs { CancelTx::initialize(&lock, datalock.clone(), datapunishablelock.clone()).unwrap(); // Set the fees according to the given strategy - BitcoinSegwitV0::set_fee(cancel.as_partial_mut(), &fee, politic).unwrap(); + BitcoinSegwitV0::set_fee( + Transaction::::as_partial_mut(&mut cancel), + &fee, + politic, + ) + .unwrap(); // // Create refund tx @@ -94,7 +99,12 @@ macro_rules! setup_txs { let mut refund = RefundTx::initialize(&cancel, new_address.clone()).unwrap(); // Set the fees according to the given strategy - BitcoinSegwitV0::set_fee(refund.as_partial_mut(), &fee, politic).unwrap(); + BitcoinSegwitV0::set_fee( + Transaction::::as_partial_mut(&mut refund), + &fee, + politic, + ) + .unwrap(); lock.verify_template(datalock.clone()).unwrap(); cancel @@ -105,16 +115,16 @@ macro_rules! setup_txs { // // Co-Sign refund // - let msg = refund - .generate_witness_message(ScriptPath::Success) - .unwrap(); + let msg = + Witnessable::::generate_witness_message(&refund, ScriptPath::Success) + .unwrap(); let sig = sign_hash(msg, &secret_a1).unwrap(); - refund.add_witness(pubkey_a1, sig).unwrap(); - let msg = refund - .generate_witness_message(ScriptPath::Success) - .unwrap(); + Witnessable::::add_witness(&mut refund, pubkey_a1, sig).unwrap(); + let msg = + Witnessable::::generate_witness_message(&refund, ScriptPath::Success) + .unwrap(); let sig = sign_hash(msg, &secret_b1).unwrap(); - refund.add_witness(pubkey_b1, sig).unwrap(); + Witnessable::::add_witness(&mut refund, pubkey_b1, sig).unwrap(); // // Finalize refund @@ -125,16 +135,16 @@ macro_rules! setup_txs { // // Co-Sign cancel // - let msg = cancel - .generate_witness_message(ScriptPath::Failure) - .unwrap(); + let msg = + Witnessable::::generate_witness_message(&cancel, ScriptPath::Failure) + .unwrap(); let sig = sign_hash(msg, &secret_a1).unwrap(); - cancel.add_witness(pubkey_a1, sig).unwrap(); - let msg = cancel - .generate_witness_message(ScriptPath::Failure) - .unwrap(); + Witnessable::::add_witness(&mut cancel, pubkey_a1, sig).unwrap(); + let msg = + Witnessable::::generate_witness_message(&cancel, ScriptPath::Failure) + .unwrap(); let sig = sign_hash(msg, &secret_b1).unwrap(); - cancel.add_witness(pubkey_b1, sig).unwrap(); + Witnessable::::add_witness(&mut cancel, pubkey_b1, sig).unwrap(); // // Finalize cancel @@ -149,19 +159,28 @@ macro_rules! setup_txs { let mut buy = BuyTx::initialize(&lock, datalock.clone(), new_address.clone()).unwrap(); // Set the fees according to the given strategy - BitcoinSegwitV0::set_fee(buy.as_partial_mut(), &fee, politic).unwrap(); + BitcoinSegwitV0::set_fee( + Transaction::::as_partial_mut(&mut buy), + &fee, + politic, + ) + .unwrap(); buy.verify_template(new_address.clone()).unwrap(); // // Co-Sign buy // - let msg = buy.generate_witness_message(ScriptPath::Success).unwrap(); + let msg = + Witnessable::::generate_witness_message(&buy, ScriptPath::Success) + .unwrap(); let sig = sign_hash(msg, &secret_a1).unwrap(); - buy.add_witness(pubkey_a1, sig).unwrap(); - let msg = buy.generate_witness_message(ScriptPath::Success).unwrap(); + Witnessable::::add_witness(&mut buy, pubkey_a1, sig).unwrap(); + let msg = + Witnessable::::generate_witness_message(&buy, ScriptPath::Success) + .unwrap(); let sig = sign_hash(msg, &secret_b1).unwrap(); - buy.add_witness(pubkey_b1, sig).unwrap(); + Witnessable::::add_witness(&mut buy, pubkey_b1, sig).unwrap(); // // Finalize buy @@ -172,9 +191,11 @@ macro_rules! setup_txs { // // Sign lock tx // - let msg = lock.generate_witness_message(ScriptPath::Success).unwrap(); + let msg = + Witnessable::::generate_witness_message(&lock, ScriptPath::Success) + .unwrap(); let sig = sign_hash(msg, &secret_a1).unwrap(); - lock.add_witness(pubkey_a1, sig).unwrap(); + Witnessable::::add_witness(&mut lock, pubkey_a1, sig).unwrap(); let lock_finalized = Broadcastable::::finalize_and_extract(&mut lock).unwrap(); @@ -186,16 +207,21 @@ macro_rules! setup_txs { PunishTx::initialize(&cancel, datapunishablelock, new_address.into()).unwrap(); // Set the fees according to the given strategy - BitcoinSegwitV0::set_fee(punish.as_partial_mut(), &fee, politic).unwrap(); + BitcoinSegwitV0::set_fee( + Transaction::::as_partial_mut(&mut punish), + &fee, + politic, + ) + .unwrap(); // // Sign punish // - let msg = punish - .generate_witness_message(ScriptPath::Failure) - .unwrap(); + let msg = + Witnessable::::generate_witness_message(&punish, ScriptPath::Failure) + .unwrap(); let sig = sign_hash(msg, &secret_a1).unwrap(); - punish.add_witness(pubkey_a1, sig).unwrap(); + Witnessable::::add_witness(&mut punish, pubkey_a1, sig).unwrap(); // // Finalize buy