diff --git a/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs b/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs index b4d2b58..987571a 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs @@ -117,7 +117,7 @@ pub fn build_escrow_transaction_output_descriptor( let descriptor = Descriptor::::new_tr(internal_agg_musig_key, Some(tap_root)) .context("Error assembling escrow output descriptor")?; descriptor.sanity_check()?; - debug!("Escrow descriptor: {:#?}", descriptor); + debug!("Escrow descriptor: {:#?}", descriptor.to_string()); Ok(descriptor) // then spend to descriptor.address(Network::Regtest) } diff --git a/taptrade-cli-demo/coordinator/src/wallet/mod.rs b/taptrade-cli-demo/coordinator/src/wallet/mod.rs index 2df1c88..aa5391e 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/mod.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/mod.rs @@ -1,4 +1,5 @@ pub mod escrow_psbt; +pub mod payout_tx; pub mod wallet_utils; // pub mod verify_tx; #[cfg(test)] diff --git a/taptrade-cli-demo/coordinator/src/wallet/payout_tx.rs b/taptrade-cli-demo/coordinator/src/wallet/payout_tx.rs new file mode 100644 index 0000000..dbb2d5c --- /dev/null +++ b/taptrade-cli-demo/coordinator/src/wallet/payout_tx.rs @@ -0,0 +1,38 @@ +use std::ops::Add; + +/// construction of the transaction spending the escrow output after a successfull trade as keyspend transaction +use super::*; +use bdk::bitcoin::psbt::PartiallySignedTransaction; +use bdk::bitcoin::OutPoint; + +fn get_tx_fees_abs_sat(blockchain_backend: &RpcBlockchain) -> Result<(u64, u64)> { + let feerate = blockchain_backend.estimate_fee(6)?; + let keyspend_payout_tx_size_vb = 140; // ~, always 1 input, 2 outputs + + let tx_fee_abs = feerate.fee_vb(keyspend_payout_tx_size_vb); + + Ok((tx_fee_abs, tx_fee_abs / 2)) +} + +impl CoordinatorWallet { + pub async fn assemble_keyspend_payout_psbt( + &self, + escrow_output: OutPoint, + payout_addresses: HashMap, + ) -> Result { + let (payout_psbt, _) = { + let wallet = self.wallet.lock().await; + let mut builder = wallet.build_tx(); + let (tx_fee_abs, tx_fee_abs_sat_per_user) = get_tx_fees_abs_sat(&self.backend)?; + + builder.add_utxo(escrow_output)?; + for (address, amount) in payout_addresses { + builder.add_recipient(address.script_pubkey(), amount - tx_fee_abs_sat_per_user); + } + builder.fee_absolute(tx_fee_abs); + + builder.finish()? + }; + Ok(payout_psbt) + } +} diff --git a/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs b/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs index f302cee..6c77b4f 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs @@ -8,14 +8,9 @@ use bdk::{ bitcoin::{psbt::Input, Network}, blockchain::RpcBlockchain, database::MemoryDatabase, - miniscript::{ - descriptor::{self}, - policy::Concrete, - Descriptor, Tap, - }, + miniscript::{policy::Concrete, Descriptor, Tap}, Wallet, }; -use sha2::digest::XofReader; async fn new_test_wallet(wallet_xprv: &str) -> CoordinatorWallet { dotenv().ok(); @@ -324,3 +319,43 @@ fn test_miniscript_compilation() { Descriptor::::new_tr(internal_key, Some(tap_tree_root)).unwrap(); dbg!(descriptor.address(bdk::bitcoin::Network::Regtest).unwrap()); } + +#[test] +fn test_create_escrow_spending_psbt() { + dotenv().ok(); + let test_descriptor = "tr(f00949d6dd1ce99a03f88a1a4f59117d553b0da51728bb7fd5b98fbf541337fb,{{and_v(v:pk(4987f3de20a9b1fa6f76c6758934953a8d615e415f1a656f0f6563694b53107d),pk(8f808f457423ff5e4e20a36d317ce9426f9da2fde875e74e15a04481b94bec06)),and_v(v:pk(f1f1db08126af105974cde6021096525ed390cf9b7cde5fedb17a0b16ed31151),pk(8f808f457423ff5e4e20a36d317ce9426f9da2fde875e74e15a04481b94bec06))},{and_v(v:and_v(v:pk(4987f3de20a9b1fa6f76c6758934953a8d615e415f1a656f0f6563694b53107d),pk(f1f1db08126af105974cde6021096525ed390cf9b7cde5fedb17a0b16ed31151)),after(2048)),and_v(v:pk(4987f3de20a9b1fa6f76c6758934953a8d615e415f1a656f0f6563694b53107d),after(12228))}})#0edq24m2"; + + let escrow_output_wallet = Wallet::new( + test_descriptor, + None, + Network::Regtest, + MemoryDatabase::new(), + ) + .unwrap(); + let secp_context = secp256k1::Secp256k1::new(); + let rpc_config = RpcConfig { + url: env::var("BITCOIN_RPC_ADDRESS_PORT").unwrap().to_string(), + auth: Auth::UserPass { + username: env::var("BITCOIN_RPC_USER").unwrap(), + password: env::var("BITCOIN_RPC_PASSWORD").unwrap(), + }, + network: Regtest, + // wallet_name: env::var("BITCOIN_RPC_WALLET_NAME")?, + wallet_name: bdk::wallet::wallet_name_from_descriptor( + test_descriptor, + None, + Network::Regtest, + &secp_context, + ) + .unwrap(), + sync_params: None, + }; + let backend = RpcBlockchain::from_config(&rpc_config).unwrap(); + escrow_output_wallet + .sync(&backend, SyncOptions::default()) + .unwrap(); + + let escrow_utxo = escrow_output_wallet.list_unspent().unwrap(); + dbg!(&escrow_utxo); + assert!(escrow_utxo.len() > 0); +}