diff --git a/docs/TapTrade_obs/.obsidian/workspace.json b/docs/TapTrade_obs/.obsidian/workspace.json index 8d91957..b5c9702 100755 --- a/docs/TapTrade_obs/.obsidian/workspace.json +++ b/docs/TapTrade_obs/.obsidian/workspace.json @@ -13,11 +13,11 @@ "state": { "type": "canvas", "state": { - "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", "viewState": { - "x": 31, - "y": -44.99563086871524, - "zoom": -0.432684559607355 + "x": 656.8883048329865, + "y": 805.4168609598363, + "zoom": -0.6000000000000001 } } } @@ -88,7 +88,7 @@ "state": { "type": "backlink", "state": { - "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -105,7 +105,7 @@ "state": { "type": "outgoing-link", "state": { - "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", "linksCollapsed": false, "unlinkedCollapsed": true } @@ -128,7 +128,7 @@ "state": { "type": "outline", "state": { - "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas" + "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas" } } } @@ -151,26 +151,29 @@ }, "active": "bdb9fd88a01a8909", "lastOpenFiles": [ - "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", + "Research/Implementation/CLI demonstrator architecture/demonstrator architecture.canvas", "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", "Research/Bitcoin fundamentals/Knowledge sources.md", + "Research/Trade Pipelines/Existing research.md", + "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", + "Research/Bitcoin fundamentals/Taproot output structure.canvas", + "Research/Bitcoin fundamentals/Spending Taproot UTXOs.md", + "Research/Implementation/Libraries.md", + "Research/Implementation/BDK.md", + "signet-instance.md", + "Project Timeline.md", + "Obsidian How-To.md", + "Research/sighash_flags.webp", + "Research/Bitcoin fundamentals/Signature and Flags.canvas", "assets/ptlc2.png", "assets/ptlc.png", "assets/Pasted image 20240611120722.png", "assets/Pasted image 20240611105421.png", - "Research/Bitcoin fundamentals/Taproot output structure.canvas", "Pasted image 20240611120722.png", "Pasted image 20240611105421.png", "Pasted image 20240605185325.png", "Research/Trade Pipelines/current trade flow.canvas", - "Research/Implementation/Libraries.md", - "Research/Bitcoin fundamentals/Spending Taproot UTXOs.md", - "Research/Bitcoin fundamentals/Signature and Flags.canvas", - "Research/Implementation/CLI demonstrator architecture/demonstrator architecture.canvas", - "Research/Implementation/Libraries.md", - "Research/Implementation/BDK.md", "Research/Implementation/UI ideas.canvas", - "Research/Trade Pipelines/Existing research.md", "Research/Implementation/CLI demonstrator architecture", "Research/Trade Pipelines/new concepts/Untitled.md" ] diff --git a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs index 15f6da0..270f723 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs @@ -4,7 +4,7 @@ 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; +use bdk::bitcoin::bip32::{ExtendedPrivKey, DerivationPath, ChildNumber}; use bdk::bitcoin::psbt::PartiallySignedTransaction; use bdk::bitcoin::secp256k1::Secp256k1; use bdk::blockchain::{ElectrumBlockchain, EsploraBlockchain}; @@ -18,6 +18,8 @@ use bdk::template::Bip86; 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 std::collections::BTreeMap; use std::str::FromStr; use std::sync::{Arc, Mutex}; @@ -114,65 +116,7 @@ pub struct CoordinatorWallet { pub backend: Arc, } -pub fn init_coordinator_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( - // &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()); - } - }; - 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( - // 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()}; - return Ok(CoordinatorWallet { - wallet: Arc::new(Mutex::new(wallet)), - backend: Arc::new(backend), - }); - }, - Err(e) => { - println!("Failed to create wallet: {}", e); - return Err(e.into()); - } - } - -} /// 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> { @@ -221,7 +165,7 @@ pub async fn fund_psbt(descriptor: Descriptor)-> Result<(), Box Result<(), Box> { +) -> Result> { // If taker is unresponsive, coordinator signs using alternative path let taker_responsive = false; // Assume taker is unresponsive if !taker_responsive { @@ -283,65 +227,157 @@ fn taker_unresponsive_psbt( // debug!("PSBT: {:?}", psbt); // println!("psbt is {:?}", psbt); let json = to_string_pretty(&psbt).unwrap(); - println!("psbt is {}", json); + // println!("psbt is {}", json); - // Ok(psbt) + Ok(psbt) } Err(e) => { println!("Error creating wallet: {:?}", e); + Err(Box::new(e)) // Convert bdk::Error to Box } } - } - Ok(()) + } 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( + // &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()); + } + }; + + 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( + // 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()) + } + } + +} + /// The `taker_unresponsive` function handles the case when the taker is unresponsive and /// the coordinator needs to sign the PSBT using an alternative path. -// TODO: Figure out how to use UTXO's -// fn taker_unresponsive( -// psbt: PartiallySignedTransaction, -// wallet: Wallet, -// // maker_utxos: Vec, -// // taker_utxos: Vec, -// recipient_address: Address, -// ) -> Result<(), Box> { -// // 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( -// "tr(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082,{and_v(v:pk(02fa55532a5ddc036db99412d050d11bf5ce4c78b9816adc3974a3c23e2a876dfe),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082)),and_v(v:pk(0219e6db0b79f8e7ee9c5fa4e77ac77e942ec3248c1a2e94c8d5ea230b13d849f0),pk(0209d4277f677aeaeeb6d3da1d66ba0dfabf296bf1609c505ad1f4cf50a870d082))})#0du8cgum", -// None, -// bdk::bitcoin::Network::Testnet, -// MemoryDatabase::new() -// ); +fn taker_unresponsive( + mut psbt: PartiallySignedTransaction, +) -> Result<(), Box> { + 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( + "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) => { - -// } -// Err(e) => { -// println!("Error creating wallet: {:?}", 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 private_key = PrivateKey::from_str(&private_key_str)?; -// let signer = SignerWrapper::new(private_key, SignerContext::Tap { is_internal_key: false }); + 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); -// wallet.add_signer( -// KeychainKind::External, -// SignerOrdering(0), -// Arc::new(signer) -// ); + // 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 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); -// } -// Ok(()) -// } + + // // 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)?; + + // 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); + + // let private_key = PrivateKey::from_str(&private)?; + let signer = SignerWrapper::new(private_key, SignerContext::Tap { is_internal_key: false }); + + println!("here"); + + 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!("{}", psbt); + } + Err(e) => { + println!("Error creating wallet: {:?}", e); + } + } + + } + Ok(()) +} #[cfg(test)] mod tests { use super::*; @@ -360,10 +396,15 @@ mod tests { Ok(descriptor) => { // println!("{}", descriptor); let _ = fund_psbt(descriptor.clone()).await; - let _ = taker_unresponsive_psbt("tb1qqw8ledhkhezru0rwj7acpker8srtcs28sng0d6"); - + 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), + Err(e) => {println!("Error: {}", e)}, } assert!(result.is_ok()); Ok(()) @@ -436,29 +477,4 @@ mod tests { // let result = blockchain.broadcast(&finalized_tx).await; // assert!(result.is_ok()); // } - - - - // #[test] - // fn test_taker_unresponsive() { - // let psbt_hex = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff040000000000000000ffffffff0100f2052a01000000160014e8e7a7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e00000000"; - // let psbt_bytes = Vec::::from_hex(psbt_hex).unwrap(); - // let psbt: BdkPsbt = deserialize(&psbt_bytes).unwrap(); - - // let wallet = Wallet::new( - // "tr(youshouldputyourdescriptorhere)", - // None, - // bdk::bitcoin::Network::Testnet, - // MemoryDatabase::new(), - // ) - // .unwrap(); - - // let maker_utxos = vec![]; - // let taker_utxos = vec![]; - - // let recipient_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap(); - - // let result = taker_unresponsive(psbt, wallet, maker_utxos, taker_utxos, recipient_address); - // assert!(result.is_ok()); - // } }