From 07e0132fbabebc72fb692346011beac98540d363 Mon Sep 17 00:00:00 2001 From: aaravm Date: Wed, 24 Jul 2024 16:36:49 +0530 Subject: [PATCH] adding coordinator_sign --- .../src/coordinator/create_taproot.rs | 722 ++++++++++-------- 1 file changed, 404 insertions(+), 318 deletions(-) diff --git a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs index 270f723..6596377 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs @@ -4,9 +4,10 @@ use anyhow::Context; /// from multiple participants, create a Taproot script descriptor, create a PSBT from the /// descriptor, and handle the case when the taker is unresponsive. use bdk::bitcoin::address::NetworkUnchecked; -use bdk::bitcoin::bip32::{ExtendedPrivKey, DerivationPath, ChildNumber}; +use bdk::bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::secp256k1::Secp256k1; +use bdk::bitcoin::{base64, PrivateKey}; use bdk::blockchain::{ElectrumBlockchain, EsploraBlockchain}; use bdk::database::MemoryDatabase; use bdk::descriptor::Descriptor; @@ -15,55 +16,29 @@ use bdk::miniscript::descriptor::TapTree; use bdk::miniscript::policy::Concrete; use bdk::miniscript::psbt::PsbtExt; use bdk::template::Bip86; +use bdk::wallet::signer::{SignerContext, SignerOrdering, SignerWrapper}; use bdk::wallet::AddressIndex; use bdk::{sled, SignOptions}; use bdk::{FeeRate, KeychainKind, SyncOptions, Wallet}; -use bdk::wallet::signer::{SignerWrapper, SignerContext, SignerOrdering}; -use bdk::bitcoin::PrivateKey; +use log::debug; +use log::info; use std::collections::BTreeMap; use std::str::FromStr; use std::sync::{Arc, Mutex}; -use log::debug; -use log::info; // use bdk::miniscript::DummyKey; use bdk::miniscript::Tap; // use crate::coordinator::create_taproot::Network; +use bdk::bitcoin::consensus::deserialize; use bdk::bitcoin::network::constants::Network; use bdk::bitcoin::Address; use serde_json::to_string_pretty; - -/// The main function in this module is `combine_and_broadcast`, which combines the PSBTs -/// from the maker and taker, finalizes the transaction, and broadcasts it on the blockchain. -pub async fn combine_and_broadcast() -> Result<(), Box> { - let mut base_psbt = PartiallySignedTransaction::from_str("TODO: insert the psbt created by the coordinator here")?; - let signed_psbts = vec![ - // TODO: Paste each participant's PSBT here - "makers_psbt", - "takers_psbt", - ]; - - for psbt in signed_psbts { - let psbt = PartiallySignedTransaction::from_str(psbt)?; - base_psbt.combine(psbt)?; - } - - let secp = Secp256k1::new(); - let psbt = base_psbt.finalize(&secp).unwrap(); - let finalized_tx = psbt.extract_tx(); - dbg!(finalized_tx.txid()); - - let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20); - dbg!(blockchain.broadcast(&finalized_tx)); - Ok(()) -} /// Other functions include `create_script`, which creates a Taproot script descriptor from async fn create_script( coordinator_key: &str, maker_key: &str, taker_key: &str, ) -> Result<(bdk::descriptor::Descriptor), Box> { - // Define policies based on the scripts provided let script_a = format!( "and(and(after(escrow_timer),{}),{})", @@ -76,19 +51,16 @@ async fn create_script( let script_c: String = format!("and(pk({}),pk({}))", maker_key, coordinator_key); let script_d = format!("and(pk({}),pk({}))", taker_key, coordinator_key); let script_e = format!("and({},after(very_long_timelock))", maker_key); - let script_f = format!( - "and_v(and_v(v:{},v:{}),after(2048))", - maker_key, taker_key - ); + let script_f = format!("and_v(and_v(v:{},v:{}),after(2048))", maker_key, taker_key); // Compile the policies // let compiled_a = Concrete::::from_str(&script_a)?.compile::()?; // let compiled_b = Concrete::::from_str(&script_b)?.compile()?; - let compiled_c = Concrete::::from_str(&script_c)?.compile::()?; + let compiled_c = Concrete::::from_str(&script_c)?.compile::()?; let compiled_d = Concrete::::from_str(&script_d)?.compile::()?; // let compiled_e = Concrete::::from_str(&script_e)?.compile()?; // let compiled_f = Concrete::::from_str(&script_f)?.compile()?; - // Create TapTree leaves + // Create TapTree leaves // let tap_leaf_a = TapTree::Leaf(Arc::new(compiled_a)); // let tap_leaf_b = TapTree::Leaf(Arc::new(compiled_b)); let tap_leaf_c = TapTree::Leaf(Arc::new(compiled_c)); @@ -96,7 +68,6 @@ async fn create_script( // let tap_leaf_e = TapTree::Leaf(Arc::new(compiled_e)); // let tap_leaf_f = TapTree::Leaf(Arc::new(compiled_f)); - // Create the TapTree (example combining leaves, adjust as necessary), will be used for Script Path Spending (Alternative Spending Paths) in the descriptor let tap_tree = TapTree::Tree(Arc::new(tap_leaf_c), Arc::new(tap_leaf_d)); @@ -116,51 +87,49 @@ pub struct CoordinatorWallet { pub backend: Arc, } - /// the provided keys, and `create_psbt`, which creates a PSBT from the descriptor /// Figure out how to put UTXO's // pub async fn fund_psbt(descriptor: Descriptor)-> Result<(PartiallySignedTransaction), Box> { -pub async fn fund_psbt(descriptor: Descriptor)-> Result<(), Box>{ - // println!("Hello create_psbt"); - // let coordinator_wallet= init_coordinator_wallet("xprv9xom13daMHDPvuivoBnceYvuhPHS6EHZZcND9nN3qvnRw8xM8Jrr24KHnARuReaX1G7PAyYxvkqTRdfhjC9MvQFPbQCXfJwiDiEfbFNSWd4"); - let wallet_result = Wallet::new( +pub async fn fund_psbt(descriptor: Descriptor) -> Result<(), Box> { + // println!("Hello create_psbt"); + // let coordinator_wallet= init_coordinator_wallet("xprv9xom13daMHDPvuivoBnceYvuhPHS6EHZZcND9nN3qvnRw8xM8Jrr24KHnARuReaX1G7PAyYxvkqTRdfhjC9MvQFPbQCXfJwiDiEfbFNSWd4"); + let wallet_result = Wallet::new( "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum", None, bdk::bitcoin::Network::Testnet, MemoryDatabase::new() ); - match wallet_result { - Ok(wallet) => { - let electrum_url = "ssl://electrum.blockstream.info:60002"; - let client = Client::new(electrum_url)?; - let blockchain = ElectrumBlockchain::from(client); + match wallet_result { + Ok(wallet) => { + let electrum_url = "ssl://electrum.blockstream.info:60002"; + let client = Client::new(electrum_url)?; + let blockchain = ElectrumBlockchain::from(client); - // Sync the wallet with the blockchain - wallet.sync(&blockchain, Default::default())?; + // Sync the wallet with the blockchain + wallet.sync(&blockchain, Default::default())?; - let new_address = wallet.get_address(AddressIndex::New).unwrap(); - println!("New wallet receiving address: {}", new_address); - // New wallet receiving address: tb1pwqvyjf2sl4znw4w8naajgl4utaxezkr06gynvzjkuesplw28qk4q4a9hl7 - // Fetch and print the wallet balance - match wallet.get_balance() { - Ok(balance) => { - println!("Wallet balance: {}", balance); - } - Err(e) => { - println!("Error fetching wallet balance: {:?}", e); - } - } - } - Err(e) => { - println!("Error creating wallet: {:?}", e); - } - } - // // Step 3: Deposit funds + let new_address = wallet.get_address(AddressIndex::New).unwrap(); + println!("New wallet receiving address: {}", new_address); + // New wallet receiving address: tb1pwqvyjf2sl4znw4w8naajgl4utaxezkr06gynvzjkuesplw28qk4q4a9hl7 + // Fetch and print the wallet balance + match wallet.get_balance() { + Ok(balance) => { + println!("Wallet balance: {}", balance); + } + Err(e) => { + println!("Error fetching wallet balance: {:?}", e); + } + } + } + Err(e) => { + println!("Error creating wallet: {:?}", e); + } + } + // // Step 3: Deposit funds // // Use some testnet faucet, such as https://bitcoinfaucet.uo1.net/send.php // // https://coinfaucet.eu/en/btc-testnet4/ - Ok(()) - + Ok(()) } fn taker_unresponsive_psbt( @@ -169,136 +138,138 @@ fn taker_unresponsive_psbt( // If taker is unresponsive, coordinator signs using alternative path let taker_responsive = false; // Assume taker is unresponsive if !taker_responsive { - let wallet_result = Wallet::new( + let wallet_result = Wallet::new( "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum", None, bdk::bitcoin::Network::Testnet, MemoryDatabase::new() ); - match wallet_result { - Ok(wallet) => { - let electrum_url = "ssl://electrum.blockstream.info:60002"; - let client = Client::new(electrum_url)?; - let blockchain = ElectrumBlockchain::from(client); + match wallet_result { + Ok(wallet) => { + let electrum_url = "ssl://electrum.blockstream.info:60002"; + let client = Client::new(electrum_url)?; + let blockchain = ElectrumBlockchain::from(client); - // Sync the wallet with the blockchain - wallet.sync(&blockchain, Default::default())?; - // Recipient address (where funds will be sent) - match wallet.get_balance() { - Ok(balance) => { - println!("Wallet balance: {}", balance); - } - Err(e) => { - println!("Error fetching wallet balance: {:?}", e); - } - } - // let unchecked_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?; - let unchecked_address = Address::from_str(taker_address).map_err(|e| { - println!("Error parsing address: {:?}", e); - e - })?; + // Sync the wallet with the blockchain + wallet.sync(&blockchain, Default::default())?; + // Recipient address (where funds will be sent) + match wallet.get_balance() { + Ok(balance) => { + println!("Wallet balance: {}", balance); + } + Err(e) => { + println!("Error fetching wallet balance: {:?}", e); + } + } + // let unchecked_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?; + let unchecked_address = Address::from_str(taker_address).map_err(|e| { + println!("Error parsing address: {:?}", e); + e + })?; - // Ensure the address is valid for the correct network (testnet in this case) - let address = unchecked_address.require_network(Network::Testnet).map_err(|e| { - println!("Error validating network: {:?}", e); - e - })?; - - // We need to specify with which policy funds will be spent. Our current wallet contains 3 - // policies: the key path spend, and two leaves in the script path spend. - let wallet_policy = wallet.policies(KeychainKind::External)?.unwrap(); - let mut path = BTreeMap::new(); - // We need to use the first leaf of the script path spend, hence the second policy - // If you're not sure what's happening here, no worries, this is bit tricky :) - // You can learn more here: https://docs.rs/bdk/latest/bdk/wallet/tx_builder/struct.TxBuilder.html#method.policy_path - path.insert(wallet_policy.id, vec![1]); + // Ensure the address is valid for the correct network (testnet in this case) + let address = unchecked_address + .require_network(Network::Testnet) + .map_err(|e| { + println!("Error validating network: {:?}", e); + e + })?; - - let mut tx_builder= wallet.build_tx(); - tx_builder - .drain_wallet() - // .add_recipient((address.script_pubkey()), 10_000) - .drain_to(address.script_pubkey()) - .fee_rate(FeeRate::from_sat_per_vb(3.0)) - .policy_path(path, KeychainKind::External); + // We need to specify with which policy funds will be spent. Our current wallet contains 3 + // policies: the key path spend, and two leaves in the script path spend. + let wallet_policy = wallet.policies(KeychainKind::External)?.unwrap(); + let mut path = BTreeMap::new(); + // We need to use the first leaf of the script path spend, hence the second policy + // If you're not sure what's happening here, no worries, this is bit tricky :) + // You can learn more here: https://docs.rs/bdk/latest/bdk/wallet/tx_builder/struct.TxBuilder.html#method.policy_path + path.insert(wallet_policy.id, vec![1]); - let (psbt, tx_details) = tx_builder.finish()?; - // debug!("PSBT: {:?}", psbt); - // println!("psbt is {:?}", psbt); - let json = to_string_pretty(&psbt).unwrap(); - // println!("psbt is {}", json); + let mut tx_builder = wallet.build_tx(); + tx_builder + .drain_wallet() + // .add_recipient((address.script_pubkey()), 10_000) + .drain_to(address.script_pubkey()) + .fee_rate(FeeRate::from_sat_per_vb(3.0)) + .policy_path(path, KeychainKind::External); - Ok(psbt) - } - Err(e) => { - println!("Error creating wallet: {:?}", e); - Err(Box::new(e)) // Convert bdk::Error to Box - } - } - } else { - Err("Taker is responsive, no need to sign using alternative path".into()) - } - } + let (psbt, tx_details) = tx_builder.finish()?; + // debug!("PSBT: {:?}", psbt); + // println!("psbt is {:?}", psbt); + let json = to_string_pretty(&psbt).unwrap(); + // println!("psbt is {}", json); -pub fn init_wallet(wallet_xprv: &str) -> Result, Box> { + Ok(psbt) + } + Err(e) => { + println!("Error creating wallet: {:?}", e); + Err(Box::new(e)) // Convert bdk::Error to Box + } + } + } else { + Err("Taker is responsive, no need to sign using alternative path".into()) + } +} + +pub fn init_wallet( + wallet_xprv: &str, +) -> Result, Box> { println!("Hello init_coordinator_wallet"); - let wallet_xprv = ExtendedPrivKey::from_str(wallet_xprv)?; - // let backend = ElectrumBlockchain::from(Client::new( + let wallet_xprv = ExtendedPrivKey::from_str(wallet_xprv)?; + // let backend = ElectrumBlockchain::from(Client::new( // &env::var("ELECTRUM_BACKEND") // .context("Parsing ELECTRUM_BACKEND from .env failed, is it set?")?, // )?); - // println!("ELECTRUM_BACKEND: {:?}", backend); - let electrum_backend = "ssl://mempool.space:40002"; - let client = match Client::new(&electrum_backend) { - Ok(c) => { - println!("Electrum client created"); - c - }, - Err(e) => { - println!("Failed to create Electrum client: {}", e); - return Err(e.into()); - } - }; + // println!("ELECTRUM_BACKEND: {:?}", backend); + let electrum_backend = "ssl://mempool.space:40002"; + let client = match Client::new(&electrum_backend) { + Ok(c) => { + println!("Electrum client created"); + c + } + Err(e) => { + println!("Failed to create Electrum client: {}", e); + return Err(e.into()); + } + }; - let backend = ElectrumBlockchain::from(client); - println!("Electrum blockchain backend created"); + let backend = ElectrumBlockchain::from(client); + println!("Electrum blockchain backend created"); // let backend = EsploraBlockchain::new(&env::var("ESPLORA_BACKEND")?, 1000); let sled_db = sled::open("./dbs/bdk-wallet")?.open_tree("default_wallet")?; println!("HELLO???"); - // let wallet = Wallet::new( + // let wallet = Wallet::new( // Bip86(wallet_xprv, KeychainKind::External), // Some(Bip86(wallet_xprv, KeychainKind::Internal)), // bdk::bitcoin::Network::Testnet, // sled_db, // )?; - let wallet_result = Wallet::new( - Bip86(wallet_xprv, KeychainKind::External), - Some(Bip86(wallet_xprv, KeychainKind::Internal)), - bdk::bitcoin::Network::Testnet, - sled_db, - ); + let wallet_result = Wallet::new( + Bip86(wallet_xprv, KeychainKind::External), + Some(Bip86(wallet_xprv, KeychainKind::Internal)), + bdk::bitcoin::Network::Testnet, + sled_db, + ); - match wallet_result { - Ok(wallet) => { - println!("Wallet created successfully"); - wallet - .sync(&backend, SyncOptions::default()) - .context("Connection to electrum server failed.")?; // we could also use Esplora to make this async - dbg!(wallet.get_balance()?); - println!{"{:?}", wallet.get_balance()}; - Ok(CoordinatorWallet { - wallet: Arc::new(Mutex::new(wallet)), - backend: Arc::new(backend), - }) - }, - Err(e) => { - println!("Failed to create wallet: {}", e); - Err(e.into()) - } - } - + match wallet_result { + Ok(wallet) => { + println!("Wallet created successfully"); + wallet + .sync(&backend, SyncOptions::default()) + .context("Connection to electrum server failed.")?; // we could also use Esplora to make this async + dbg!(wallet.get_balance()?); + println! {"{:?}", wallet.get_balance()}; + Ok(CoordinatorWallet { + wallet: Arc::new(Mutex::new(wallet)), + backend: Arc::new(backend), + }) + } + Err(e) => { + println!("Failed to create wallet: {}", e); + Err(e.into()) + } + } } /// The `taker_unresponsive` function handles the case when the taker is unresponsive and @@ -306,175 +277,290 @@ pub fn init_wallet(wallet_xprv: &str) -> Result, B fn taker_unresponsive( mut psbt: PartiallySignedTransaction, ) -> Result<(), Box> { - println!("here"); + println!("here"); // If taker is unresponsive, coordinator signs using alternative path let taker_responsive = false; // Assume taker is unresponsive if !taker_responsive { - let wallet_result = Wallet::new( + let wallet_result = Wallet::new( "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum", None, bdk::bitcoin::Network::Testnet, MemoryDatabase::new() ); - match wallet_result { - Ok(mut wallet) => { - let electrum_url = "ssl://electrum.blockstream.info:60002"; - let client = Client::new(electrum_url)?; - let blockchain = ElectrumBlockchain::from(client); + match wallet_result { + Ok(mut wallet) => { + let electrum_url = "ssl://electrum.blockstream.info:60002"; + let client = Client::new(electrum_url)?; + let blockchain = ElectrumBlockchain::from(client); - // Sync the wallet with the blockchain - wallet.sync(&blockchain, Default::default())?; - // Recipient address (where funds will be sent) - match wallet.get_balance() { - Ok(balance) => { - println!("Wallet balance: {}", balance); - } - Err(e) => { - println!("Error fetching wallet balance: {:?}", e); - } - } - println!("here"); + // Sync the wallet with the blockchain + wallet.sync(&blockchain, Default::default())?; + // Recipient address (where funds will be sent) + match wallet.get_balance() { + Ok(balance) => { + println!("Wallet balance: {}", balance); + } + Err(e) => { + println!("Error fetching wallet balance: {:?}", e); + } + } + println!("here"); + // // Step 2: Add the BDK signer + // let mut private_key_str = String::new(); + // File::open("key.txt")?.read_to_string(&mut private_key_str)?; + // println!("{}", private_key_str); + let xprv_str = "xprv9yuQwS7jc4QpSGTiv5WF2kDjuJh3eXzM1Qa4MWYRJLrsdVEydPxz7psF9YKtyPi72dWk48qwHTjiZL1QYTegT4jQcQxR3XDZtb5ijuH8fjN"; + let secp = Secp256k1::new(); - // // Step 2: Add the BDK signer - // let mut private_key_str = String::new(); - // File::open("key.txt")?.read_to_string(&mut private_key_str)?; - // println!("{}", private_key_str); - let xprv_str = "xprv9yu6ywkjYByRSjaivcWau9vXmwXz8MpwhAMNqwgAqeeXjFTiWuxZpz69LjXTauM4nqYuQpEwPbZ71tKK7hvwehK5k8U6JWW8v9odujQx2en"; - let secp = Secp256k1::new(); - - // Parse the xprv key - let xprv = ExtendedPrivKey::from_str(xprv_str)?; + // Parse the xprv key + let xprv = ExtendedPrivKey::from_str(xprv_str)?; - // Derive the private key (you can adjust the derivation path as needed) - let derivation_path = DerivationPath::from(vec![ChildNumber::from(0)]); - let derived_priv_key = xprv.derive_priv(&secp, &derivation_path)?; + // Derive the private key (you can adjust the derivation path as needed) + let derivation_path = DerivationPath::from(vec![ChildNumber::from(0)]); + let derived_priv_key = xprv.derive_priv(&secp, &derivation_path)?; - // Convert the derived key to a PrivateKey - let private_key = PrivateKey::new(derived_priv_key.private_key, Network::Bitcoin); + // Convert the derived key to a PrivateKey + let private_key = PrivateKey::new(derived_priv_key.private_key, Network::Bitcoin); - // let private_key = PrivateKey::from_str(&private)?; - let signer = SignerWrapper::new(private_key, SignerContext::Tap { is_internal_key: false }); + // Ensure the private key is correctly derived + println!("Private key: {:?}", private_key); - println!("here"); + // let private_key = PrivateKey::from_str(&private)?; + let signer = SignerWrapper::new( + private_key, + SignerContext::Tap { + is_internal_key: false, + }, + ); - wallet.add_signer( - KeychainKind::External, - SignerOrdering(0), - Arc::new(signer) - ); + println!("here"); - // // Step 3: Sign the transaction - // let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?; - let finalized = wallet.sign(&mut psbt, SignOptions::default()); - println!("{}", psbt); - } - Err(e) => { - println!("Error creating wallet: {:?}", e); - } - } - + wallet.add_signer(KeychainKind::External, SignerOrdering(0), Arc::new(signer)); + + // // Step 3: Sign the transaction + // let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?; + let finalized = wallet.sign(&mut psbt, SignOptions::default()); + println!("taker unresponsive psbt is: {}", psbt); + } + Err(e) => { + println!("Error creating wallet: {:?}", e); + } + } } Ok(()) } + +fn coordinator_sign( + mut psbt: PartiallySignedTransaction, +) -> Result<(), Box> { + println!("here in coordinator_sign"); + + let wallet_result = Wallet::new( + "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum", + None, + bdk::bitcoin::Network::Testnet, + MemoryDatabase::new() + ); + + match wallet_result { + Ok(mut wallet) => { + let electrum_url = "ssl://electrum.blockstream.info:60002"; + let client = Client::new(electrum_url)?; + let blockchain = ElectrumBlockchain::from(client); + + // Sync the wallet with the blockchain + wallet.sync(&blockchain, Default::default())?; + // Recipient address (where funds will be sent) + match wallet.get_balance() { + Ok(balance) => { + println!("Wallet balance: {}", balance); + } + Err(e) => { + println!("Error fetching wallet balance: {:?}", e); + } + } + // // Step 2: Add the BDK signer + // let mut private_key_str = String::new(); + // File::open("key.txt")?.read_to_string(&mut private_key_str)?; + // println!("{}", private_key_str); + let xprv_str = "xprv9yu6ywkjYByRSjaivcWau9vXmwXz8MpwhAMNqwgAqeeXjFTiWuxZpz69LjXTauM4nqYuQpEwPbZ71tKK7hvwehK5k8U6JWW8v9odujQx2en"; + let secp = Secp256k1::new(); + + // Parse the xprv key + let xprv = ExtendedPrivKey::from_str(xprv_str)?; + println!("Parsed xprv: {:?}", xprv); + println!("Base58 xprv: {:?}", xprv.to_string()); + + // Derive the private key (you can adjust the derivation path as needed) + let derivation_path = DerivationPath::from(vec![ChildNumber::from(0)]); + let derived_priv_key = xprv.derive_priv(&secp, &derivation_path)?; + println!("Derived private key: {:?}", derived_priv_key); + println!( + "Base58 derived_priv_key: {:?}", + derived_priv_key.to_string() + ); + + // Convert the derived key to a PrivateKey + let private_key = PrivateKey::new(derived_priv_key.private_key, Network::Bitcoin); + + // let private_key = PrivateKey::from_str(&private)?; + let signer = SignerWrapper::new( + private_key, + SignerContext::Tap { + is_internal_key: false, + }, + ); + + wallet.add_signer(KeychainKind::External, SignerOrdering(0), Arc::new(signer)); + + // Print the PSBT before signing + println!("PSBT before signing: {:?}", psbt); + + // // Step 3: Sign the transaction + // let mut psbt = PartiallySignedTransaction::from_str("TODO: paste the PSBT obtained in step 3 here")?; + let finalized = wallet.sign(&mut psbt, SignOptions::default()); + println!("Finalized: {:?}", finalized); + println!("Coordinator psbt after signing is {}", psbt); + } + Err(e) => { + println!("Error creating wallet: {:?}", e); + } + } + + Ok(()) +} + +/// The main function in this module is `combine_and_broadcast`, which combines the PSBTs +/// from the maker and taker, finalizes the transaction, and broadcasts it on the blockchain. +pub async fn combine_and_broadcast() -> Result<(), Box> { + let mut base_psbt = PartiallySignedTransaction::from_str( + "TODO: insert the psbt created by the coordinator here", + )?; + let signed_psbts = vec![ + // TODO: Paste each participant's PSBT here + "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", + ]; + + for psbt in signed_psbts { + let psbt = PartiallySignedTransaction::from_str(psbt)?; + base_psbt.combine(psbt)?; + } + + let secp = Secp256k1::new(); + let psbt = base_psbt.finalize(&secp).unwrap(); + let finalized_tx = psbt.extract_tx(); + dbg!(finalized_tx.txid()); + + let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20); + dbg!(blockchain.broadcast(&finalized_tx)); + Ok(()) +} + #[cfg(test)] mod tests { - use super::*; - use bdk::{bitcoin::bip32::ExtendedPrivKey, descriptor}; - use bitcoin::consensus::deserialize; - use bdk::blockchain::ElectrumBlockchain; - use bdk::template::Bip86; - use std::env; - use anyhow::{Context, Error}; - use bdk::sled; + use super::*; + use anyhow::{Context, Error}; + use bdk::blockchain::ElectrumBlockchain; + use bdk::sled; + use bdk::template::Bip86; + use bdk::{bitcoin::bip32::ExtendedPrivKey, descriptor}; + use bitcoin::consensus::deserialize; + use std::env; - #[tokio::test] - async fn test_fund_psbt()-> Result<(), Error> { - let result = create_descriptor().await; - match &result{ - Ok(descriptor) => { - // println!("{}", descriptor); - let _ = fund_psbt(descriptor.clone()).await; - let psbt = taker_unresponsive_psbt("tb1qqw8ledhkhezru0rwj7acpker8srtcs28sng0d6"); - match &psbt { - Ok(psbt)=>{ - let _ = taker_unresponsive(psbt.clone()); - } - Err(e) => {println!("Error: {}", e)}, - } - }, - Err(e) => {println!("Error: {}", e)}, - } - assert!(result.is_ok()); - Ok(()) - } + #[tokio::test] + async fn test_fund_psbt() -> Result<(), Error> { + let result = create_descriptor().await; + match &result { + Ok(descriptor) => { + // println!("{}", descriptor); + let _ = fund_psbt(descriptor.clone()).await; + let psbt = taker_unresponsive_psbt("tb1qqw8ledhkhezru0rwj7acpker8srtcs28sng0d6"); + match &psbt { + Ok(psbt) => { + println!("psbt is {:?}", psbt.clone()); + let _ = taker_unresponsive(psbt.clone()); + let _ = coordinator_sign(psbt.clone()); + } + Err(e) => { + println!("Error: {}", e) + } + } + } + Err(e) => { + println!("Error: {}", e) + } + } + assert!(result.is_ok()); + Ok(()) + } - async fn create_descriptor() -> Result, Box>{ - let coordinator_pub = "0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082"; - let coordinator_xpub = "xpub6C3kuZk67kPgw2evdJ72ckEARaqjwtx62KZY4t4YR6AsqJrsFSnDNm5sh9FkfdHLcXNWgcwAZs2prhNj23xG5Ui1pwyW1mtcGfEtBQdmima"; - let maker_pub = "02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe"; - let taker_pub = "0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0"; + async fn create_descriptor() -> Result, Box> { + let coordinator_pub = "0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082"; + let coordinator_xpub = "xpub6C3kuZk67kPgw2evdJ72ckEARaqjwtx62KZY4t4YR6AsqJrsFSnDNm5sh9FkfdHLcXNWgcwAZs2prhNj23xG5Ui1pwyW1mtcGfEtBQdmima"; + let maker_pub = "02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe"; + let taker_pub = "0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0"; - let result = create_script(&coordinator_pub, maker_pub, taker_pub).await; - match result { - Ok(descriptor) => { - // println!("{}", descriptor); - Ok(descriptor) - }, - Err(e) => { - println!("Error: {}", e); - Err(e) - }, - } -} -// https://github.com/danielabrozzoni/multisigs_and_carrots/tree/master - #[tokio::test] - async fn test_create_script()-> Result<(), Error>{ - // Taking public key using https://iancoleman.io/bip39/ that generates addresses and respective public key by the seed phrase of wallet (Using sparrow wallet) - - let result = create_descriptor().await; - match &result{ - Ok(descriptor) => { - println!("{}", descriptor); - }, - Err(e) => println!("Error: {}", e), - } - assert!(result.is_ok()); - Ok(()) - // tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum - } - - - // #[tokio::test] - // async fn test_combine_and_broadcast() { - // // Create a base PSBT - // let base_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; - // let base_psbt_bytes = Vec::::from_hex(base_psbt_hex).unwrap(); - // let base_psbt: PartiallySignedTransaction = deserialize(&base_psbt_bytes).unwrap(); - - // // Create signed PSBTs - // let maker_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; - // let maker_psbt_bytes = Vec::::from_hex(maker_psbt_hex).unwrap(); - // let maker_psbt: PartiallySignedTransaction = deserialize(&maker_psbt_bytes).unwrap(); - - // let taker_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; - // let taker_psbt_bytes = Vec::::from_hex(taker_psbt_hex).unwrap(); - // let taker_psbt: PartiallySignedTransaction = deserialize(&taker_psbt_bytes).unwrap(); - - // // Combine the PSBTs - // let mut combined_psbt = base_psbt.clone(); - // combined_psbt.combine(maker_psbt).unwrap(); - // combined_psbt.combine(taker_psbt).unwrap(); - - // // Finalize the transaction - // let secp = Secp256k1::new(); - // let finalized_psbt = combined_psbt.finalize(&secp).unwrap(); - // let finalized_tx = finalized_psbt.extract_tx(); - - // // Broadcast the transaction - // let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20); - // let result = blockchain.broadcast(&finalized_tx).await; - // assert!(result.is_ok()); - // } + let result = create_script(&coordinator_pub, maker_pub, taker_pub).await; + match result { + Ok(descriptor) => { + // println!("{}", descriptor); + Ok(descriptor) + } + Err(e) => { + println!("Error: {}", e); + Err(e) + } + } + } + // https://github.com/danielabrozzoni/multisigs_and_carrots/tree/master + #[tokio::test] + async fn test_create_script() -> Result<(), Error> { + // Taking public key using https://iancoleman.io/bip39/ that generates addresses and respective public key by the seed phrase of wallet (Using sparrow wallet) + + let result = create_descriptor().await; + match &result { + Ok(descriptor) => { + println!("{}", descriptor); + } + Err(e) => println!("Error: {}", e), + } + assert!(result.is_ok()); + Ok(()) + // tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum + } + + // #[tokio::test] + // async fn test_combine_and_broadcast() { + // // Create a base PSBT + // let base_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; + // let base_psbt_bytes = Vec::::from_hex(base_psbt_hex).unwrap(); + // let base_psbt: PartiallySignedTransaction = deserialize(&base_psbt_bytes).unwrap(); + + // // Create signed PSBTs + // let maker_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; + // let maker_psbt_bytes = Vec::::from_hex(maker_psbt_hex).unwrap(); + // let maker_psbt: PartiallySignedTransaction = deserialize(&maker_psbt_bytes).unwrap(); + + // let taker_psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; + // let taker_psbt_bytes = Vec::::from_hex(taker_psbt_hex).unwrap(); + // let taker_psbt: PartiallySignedTransaction = deserialize(&taker_psbt_bytes).unwrap(); + + // // Combine the PSBTs + // let mut combined_psbt = base_psbt.clone(); + // combined_psbt.combine(maker_psbt).unwrap(); + // combined_psbt.combine(taker_psbt).unwrap(); + + // // Finalize the transaction + // let secp = Secp256k1::new(); + // let finalized_psbt = combined_psbt.finalize(&secp).unwrap(); + // let finalized_tx = finalized_psbt.extract_tx(); + + // // Broadcast the transaction + // let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20); + // let result = blockchain.broadcast(&finalized_tx).await; + // assert!(result.is_ok()); + // } }