From 9dd0533d324b99357c3897b6ff50c63f271ca748 Mon Sep 17 00:00:00 2001 From: fbock Date: Tue, 27 Aug 2024 09:32:02 +0200 Subject: [PATCH] comment database functions --- .../src/coordinator/create_taproot.rs.unused | 597 ------------------ .../coordinator/src/database/mod.rs | 107 ++-- taptrade-cli-demo/coordinator/src/main.rs | 9 +- 3 files changed, 46 insertions(+), 667 deletions(-) delete mode 100644 taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs.unused diff --git a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs.unused b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs.unused deleted file mode 100644 index 4a25d71..0000000 --- a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs.unused +++ /dev/null @@ -1,597 +0,0 @@ -use anyhow::Context; -/// This module contains functions related to creating and broadcasting Taproot transactions. -/// It includes functions to combine and broadcast the partially signed transactions (PSBTs) -/// 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::{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; -use bdk::electrum_client::Client; -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 log::debug; -use log::info; -use std::collections::BTreeMap; -use std::str::FromStr; -use std::sync::{Arc, Mutex}; -// 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; - -/// 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),{}),{})", - maker_key, coordinator_key - ); - let script_b = format!( - "and_v(v:{},and_v(v:{},{}))", - maker_key, taker_key, coordinator_key - ); - 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); - // 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_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 - // 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)); - let tap_leaf_d = TapTree::Leaf(Arc::new(compiled_d)); - // 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)); - - // An internal key, that defines the way to spend the transaction directly, using Key Path Spending - let dummy_internal_key = coordinator_key.to_string(); - - // Create the descriptor - let descriptor = Descriptor::new_tr(dummy_internal_key, Some(tap_tree))?; - // debug!("descriptor is {:}", descriptor); - - Ok(descriptor) -} - -#[derive(Clone)] -pub struct CoordinatorWallet { - pub wallet: Arc>>, - 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( - "tr(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA,{and_v(v:pk(tpubDDk7P4EjBobe6VbQnW15wCtEm4sfDHiS4Yg3tCS9LQ55x8duoTvELtRJCc4rjdsDeq6FMTW8Sjdy34PC43dY93E2wEn2TytrM8D6FhLtqg2),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA)),and_v(v:pk(tpubDDpD87xvUwFdswsQTXsjkBPWXThYZABDsGP9JbzDatobQzBn2pvPrP4B7MBLTHmTdpx1zuTM4TjB5X31WrLvexsvV4ayGStKF4mxa4gct6y),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA))})#mpnyppnp", - 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); - - // 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 - // // Use some testnet faucet, such as https://bitcoinfaucet.uo1.net/send.php - // // https://coinfaucet.eu/en/btc-testnet4/ - Ok(()) -} - -fn taker_unresponsive_psbt( - taker_address: &str, -) -> Result> { - // 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(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA,{and_v(v:pk(tpubDDk7P4EjBobe6VbQnW15wCtEm4sfDHiS4Yg3tCS9LQ55x8duoTvELtRJCc4rjdsDeq6FMTW8Sjdy34PC43dY93E2wEn2TytrM8D6FhLtqg2),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA)),and_v(v:pk(tpubDDpD87xvUwFdswsQTXsjkBPWXThYZABDsGP9JbzDatobQzBn2pvPrP4B7MBLTHmTdpx1zuTM4TjB5X31WrLvexsvV4ayGStKF4mxa4gct6y),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA))})#mpnyppnp", - 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); - - // 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]); - - 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); - - let (psbt, _tx_details) = tx_builder.finish()?; - // debug!("PSBT: {:?}", psbt); - println!("psbt is main {:?}", psbt); - let json = to_string_pretty(&psbt).unwrap(); - // println!("psbt is {}", json); - - 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( - // &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. -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(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA,{and_v(v:pk(tpubDDk7P4EjBobe6VbQnW15wCtEm4sfDHiS4Yg3tCS9LQ55x8duoTvELtRJCc4rjdsDeq6FMTW8Sjdy34PC43dY93E2wEn2TytrM8D6FhLtqg2),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA)),and_v(v:pk(tpubDDpD87xvUwFdswsQTXsjkBPWXThYZABDsGP9JbzDatobQzBn2pvPrP4B7MBLTHmTdpx1zuTM4TjB5X31WrLvexsvV4ayGStKF4mxa4gct6y),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA))})#mpnyppnp", - 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); - } - } - 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); - // taker xprv - let xprv_str = "tprv8inPcmXmKnA9ZA9TURpvvva3i7JDsKEmcXAcAkccVPJtur4p8dgFCLsyYoGe6oNiKoKLyRvJTAUXHUbHC2i7LHFg6dFCLfvrfqa5k1ajTGQ"; - 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); - - // Ensure the private key is correctly derived - println!("Private key: {:?}", private_key); - - // 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!("taker unresponsive psbt 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(()) -} - -fn coordinator_sign( - mut psbt: PartiallySignedTransaction, -) -> Result<(), Box> { - println!("here in coordinator_sign"); - - let wallet_result = Wallet::new( - "tr(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA,{and_v(v:pk(tpubDDk7P4EjBobe6VbQnW15wCtEm4sfDHiS4Yg3tCS9LQ55x8duoTvELtRJCc4rjdsDeq6FMTW8Sjdy34PC43dY93E2wEn2TytrM8D6FhLtqg2),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA)),and_v(v:pk(tpubDDpD87xvUwFdswsQTXsjkBPWXThYZABDsGP9JbzDatobQzBn2pvPrP4B7MBLTHmTdpx1zuTM4TjB5X31WrLvexsvV4ayGStKF4mxa4gct6y),pk(tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA))})#mpnyppnp", - 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 = "tprv8iseuSeeGfkpgpV1kK7HBMXFPUp8j8Repu7pGqp6S1fn58snqXUYsNjwVXxgFVN2wt8mtdHcmyjTQKD4F34k3ATozjm8QA66xLUBstpJVKH"; - 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); - for (i, input) in (0_u32..).zip(psbt.inputs.iter()) { - println!("Input: {:?}", input); - let input_script = input.witness_utxo.as_ref().unwrap().script_pubkey.clone(); - let address = wallet.get_address(bdk::wallet::AddressIndex::Peek(i))?; - if input_script != address.script_pubkey() { - println!("Input {} does not correspond to the wallet", i); - } - } - - // // 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()); - match finalized { - Ok(_) => { - println!("Successfully signed PSBT."); - println!("Final PSBT: {:?}", psbt); - } - Err(e) => { - println!("Error signing PSBT: {:?}", e); - } - } - } - Err(e) => { - println!("Error creating wallet: {:?}", e); - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use crate::coordinator; - - 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 is: {}", 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 = "03f294ab32537a49f7dc07b49249b4add8cad82cd1f7b7b4a9db6fb0947dc7b755"; - let _maker_pub = "0349ba2df1a252ec57a53d329edc076045ec621334471b19dd54a91054451e2fce"; - let _taker_pub = "023bb7fc7bf8482fa7a509b24af286a83ce3316036ef97408cdc575d18df387166"; - - let _coordinator_xpub= "xpub6FC4XcsChmV3JVKxBmnnNqqzdPEQu57Vra8JCooFWNiHALTrPhT3uRgyReYQuCUTutYNB7X9wSsTuLgYQvSbXFwEfJAefM3msGexe4V8rRz"; - let _coordinator_xprv= "xprvA2Ci87LJsPvk61FV5kFn1huG5MPvVcPeVMChQRPdx3BJHY8hrA8oMdNVaMo2F7yiaSbztXfrcd9ewTfK7pioE7CDU6YpjoN43EimS76xhqB"; - let _coordinator_tpub= "tpubDDD58oLVWb7GbbvS4q8DW1wnTgJD1ZNS1p9wgeb1G3zDqTEohjGWbeDcmJNc2S1TJ1FW6hLYG1odBJwp1BhvzGQLhHKjWSHm6E6NzvvLjiA"; - let _coordinator_tprv= "tprv8iseuSeeGfkpgpV1kK7HBMXFPUp8j8Repu7pGqp6S1fn58snqXUYsNjwVXxgFVN2wt8mtdHcmyjTQKD4F34k3ATozjm8QA66xLUBstpJVKH"; - - let _taker_xpub= "xpub6F9EN4dLoTNy5SS3y47TmcY8Y1aDizAkEukEwPEL23NiybeBodokP8gkCWeqvVjaAdn5F56nRtsbg9Jv59NawfgwoD1yypW9WezwZL6YnLn"; - let _taker_xprv= "xprvA29sxZ6Sy5pfrxMas2aTQUbPyyjjKXStsgpe8zpiThqk6oK3G6VVqLNGMCLsYJ817KvxU6wZmttcJ98QgvAgEbbSZdnaVABxjWHXHM29jnU"; - let _taker_tpub= "tpubDDpD87xvUwFdswsQTXsjkBPWXThYZABDsGP9JbzDatobQzBn2pvPrP4B7MBLTHmTdpx1zuTM4TjB5X31WrLvexsvV4ayGStKF4mxa4gct6y"; - let _taker_tprv= "tprv8ippjtQnNMekTmb7XbRxa8DPJ79wZ3UuDEjm1RFAwgLDtQ48FTqFM5jiGNWXYfWKUmTjUCZKwFUQkzg9p8Wd3es36Gzt9Wv1ec2wj2nNUCD"; - - let _maker_xpub= "xpub6F6oEwkKkstNApzPutWS8Qtnx1iW3FvceCB66ibmZkMQ13esgoekEPq1UuH3wmGdhtQrCGeP7HxkbpuK9WDWWCKTMow321MsH3fhJjUMnAk"; - let _maker_xprv= "xprvA27SqSDRvWL4xLuvoryRmGx4Pyt1doCmGyFVJLCA1QpR8FKj9GLVgbWXdd6z6RzPxMnZyLJYHotipd3Y4pNAXDz5Zz2tgKCokjpfJGnvFBU"; - let _maker_tpub= "tpubDDk7P4EjBobe6VbQnW15wCtEm4sfDHiS4Yg3tCS9LQ55x8duoTvELtRJCc4rjdsDeq6FMTW8Sjdy34PC43dY93E2wEn2TytrM8D6FhLtqg2"; - let _maker_tprv= "tprv8inPcmXmKnA9ZA9TURpvvva3i7JDsKEmcXAcAkccVPJtur4p8dgFCLsyYoGe6oNiKoKLyRvJTAUXHUbHC2i7LHFg6dFCLfvrfqa5k1ajTGQ"; - - let result = create_script(_coordinator_tpub, _maker_tpub, _taker_tpub).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()); - // } -} diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index 6b71844..4c772cf 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -68,6 +68,7 @@ impl CoordinatorDB { .map_err(|e| anyhow!("Failed to connect to SQLite database: {}", e))?; // Create the trades table if it doesn't exist + // this table contains requests of makers awaiting submission of bond sqlx::query( // robohash is hash as bytes "CREATE TABLE IF NOT EXISTS maker_requests ( @@ -82,6 +83,8 @@ impl CoordinatorDB { ) .execute(&db_pool) .await?; + + // this table contains offers that are active in the orderbook awaiting a taker sqlx::query( // robohash is hash as bytes "CREATE TABLE IF NOT EXISTS active_maker_offers ( @@ -106,6 +109,7 @@ impl CoordinatorDB { .execute(&db_pool) .await?; + // this table contains offers that are taken and are in the trade process sqlx::query( "CREATE TABLE IF NOT EXISTS taken_offers ( offer_id TEXT PRIMARY KEY, @@ -157,6 +161,7 @@ impl CoordinatorDB { }) } + /// insert a new maker request to create an offer in the table pub async fn insert_new_maker_request( &self, order: &OfferRequest, @@ -180,6 +185,7 @@ impl CoordinatorDB { Ok(()) } + /// fetch the bond requirements for a maker request pub async fn fetch_bond_requirements(&self, robohash: &String) -> Result { let maker_request = sqlx::query( "SELECT bond_address, bond_amount_sat, amount_sat FROM maker_requests WHERE robohash = ?", @@ -195,6 +201,7 @@ impl CoordinatorDB { }) } + /// deletes the maker offer from the pending table and returns it async fn fetch_and_delete_offer_from_bond_table( &self, robohash_hex: &str, @@ -227,6 +234,7 @@ impl CoordinatorDB { Ok(awaiting_bond_offer) } + /// on reciept of a valid bond this will fetch the offer from the pending table and insert it into the active trades table (orderbook) pub async fn move_offer_to_active( &self, data: &BondSubmissionRequest, @@ -270,6 +278,7 @@ impl CoordinatorDB { Ok(remaining_offer_information.offer_duration_ts) } + /// called by the taker, returns offers that match the taker's request pub async fn fetch_suitable_offers( &self, requested_offer: &OffersRequest, @@ -305,6 +314,7 @@ impl CoordinatorDB { Ok(Some(available_offers)) } + /// fetches the bond requirements for the taker pub async fn fetch_taker_bond_requirements( &self, offer_id_hex: &str, @@ -324,6 +334,7 @@ impl CoordinatorDB { }) } + /// used to fetch and delete the offer from the orderbook (active_maker_offers) table async fn fetch_and_delete_offer_from_public_offers_table( &self, offer_id_hex: &str, @@ -359,17 +370,20 @@ impl CoordinatorDB { }) } + /// once the taker submitted his bond the offer is moved to the taken_offers table (removed from the orderbook) pub async fn add_taker_info_and_move_table( &self, trade_and_taker_info: &OfferPsbtRequest, escrow_tx_data: &EscrowPsbt, ) -> Result<()> { + // this fetches the offer and deletes it from the orderbook let public_offer = self .fetch_and_delete_offer_from_public_offers_table( &trade_and_taker_info.offer.offer_id_hex, ) .await?; + // insert the offer into the taken_offers table sqlx::query( "INSERT OR REPLACE INTO taken_offers (offer_id, robohash_maker, robohash_taker, is_buy_order, amount_sat, bond_ratio, offer_duration_ts, bond_address_maker, bond_address_taker, bond_amount_sat, bond_tx_hex_maker, @@ -414,6 +428,7 @@ impl CoordinatorDB { Ok(()) } + /// fetches the escrow psbt from the db for the given offer pub async fn fetch_escrow_output_information( &self, offer_id_hex: &str, @@ -451,8 +466,8 @@ impl CoordinatorDB { })) } - // returns a hashmap of RoboHash, MonitoringBond for the monitoring loop - // in case this gets a bottleneck (db too large for heap) we can implement in place checking + /// returns a hashmap of RoboHash, MonitoringBond for the monitoring loop + /// in case this gets a bottleneck (db too large for heap) we can implement in place checking pub async fn fetch_all_bonds(&self) -> Result> { let mut bonds = Vec::new(); let mut rows_orderbook = sqlx::query( @@ -478,58 +493,10 @@ impl CoordinatorDB { }; bonds.push(bond); } - - // we shouldn't need this as bonds will be locked onchain when trade is taken and we should - // move to taken_offers only once everything is confirmed - // let mut rows_taken = sqlx::query( - // "SELECT offer_id, robohash_maker, robohash_taker, - // bond_address_maker, bond_address_taker, bond_amount_sat, amount_sat, bond_tx_hex_maker, bond_tx_hex_taker - // FROM taken_offers", - // ) - // .fetch(&*self.db_pool); - - // while let Some(row) = rows_taken.next().await { - // let row = row?; - - // let robohash_maker: Vec = row.get("robohash_maker"); - // let robohash_taker: Vec = row.get("robohash_taker"); - // let locking_amount_sat = row.get::("bond_amount_sat") as u64; - // let min_input_sum_sat = row.get::("amount_sat") as u64; - // let trade_id_hex: String = row.get("offer_id"); - - // let requirements_maker = BondRequirements { - // bond_address: row.get("bond_address_maker"), - // locking_amount_sat, - // min_input_sum_sat, - // }; - - // let bond_maker = MonitoringBond { - // bond_tx_hex: row.get("bond_tx_hex_maker"), - // robot: robohash_maker, - // trade_id_hex: trade_id_hex.clone(), - // requirements: requirements_maker, - // table: Table::ActiveTrades, - // }; - // bonds.push(bond_maker); - - // let requirements_maker = BondRequirements { - // bond_address: row.get("bond_address_taker"), - // locking_amount_sat, - // min_input_sum_sat, - // }; - - // let bond_taker = MonitoringBond { - // bond_tx_hex: row.get("bond_tx_hex_taker"), - // trade_id_hex, - // robot: robohash_taker, - // requirements: requirements_maker, - // table: Table::ActiveTrades, - // }; - // bonds.push(bond_taker); - // } Ok(bonds) } + /// removes an offer from the orderbook (active_maker_offers) table, gets called when a bond violation is detected pub async fn remove_violating_bond(&self, bond: &MonitoringBond) -> Result<()> { if bond.table == Table::Orderbook { sqlx::query("DELETE FROM active_maker_offers WHERE offer_id = ?") @@ -542,22 +509,11 @@ impl CoordinatorDB { "Invalid table type when trying to remove violating bond from db" )); } - - // we shouldn't need this as bonds will be locked onchain when trade is taken and we should - // move to taken_offers only once everything is confirmed - // } else if bond.table == Table::ActiveTrades { - // sqlx::query("DELETE FROM taken_offers WHERE offer_id = ?") - // .bind(bond.trade_id_hex) - // .execute(&*self.db_pool) - // .await?; - - // sqlx::query("DELETE FROM active_maker_offers WHERE offer_id = ?") - // .bind(trade_id_hex) - // .execute(&*self.db_pool) - // .await?; Ok(()) } + /// fetches all txids of escrow transactions that have the flag escrow_psbt_is_confirmed set to 0 + /// used to check if theses txids are confirmed onchain pub async fn fetch_unconfirmed_escrow_txids(&self) -> Result> { let mut txids = Vec::new(); let mut rows = sqlx::query( @@ -572,6 +528,7 @@ impl CoordinatorDB { Ok(txids) } + /// sets all passed escrow txids to confirmed pub async fn confirm_bond_txids(&self, confirmed_txids: Vec) -> Result<()> { for txid in confirmed_txids { sqlx::query( @@ -584,6 +541,7 @@ impl CoordinatorDB { Ok(()) } + /// used to check if txid is set to confirmed in the db pub async fn get_txid_confirmation_status(&self, txid: &String) -> Result { let status = sqlx::query( "SELECT escrow_psbt_is_confirmed FROM taken_offers WHERE escrow_psbt_txid = ?", @@ -594,6 +552,7 @@ impl CoordinatorDB { Ok(status.get::("escrow_psbt_is_confirmed") == 1) } + /// used to verify that a robohash/user id is actually part of a trade and contained in the table pub async fn is_valid_robohash_in_table( &self, robohash_hex: &str, @@ -611,6 +570,7 @@ impl CoordinatorDB { Ok(robohash.is_some()) } + /// used to check if a user id / robohash is the maker or taker (true if maker, false if taker) async fn is_maker_in_taken_offers(&self, offer_id: &str, robohash_hex: &str) -> Result { let robohash_bytes = hex::decode(robohash_hex)?; @@ -631,6 +591,7 @@ impl CoordinatorDB { Ok(is_maker) } + /// insert a returned, signed escrow psbt into the db, if it was already existent return false, else return true if inserted pub async fn insert_signed_escrow_psbt( &self, signed_escow_psbt_data: &PsbtSubmissionRequest, @@ -686,6 +647,7 @@ impl CoordinatorDB { } } + /// used to fetch both signed escrow locking psbts from the db pub async fn fetch_both_signed_escrow_psbts( &self, offer_id_hex: &str, @@ -706,6 +668,7 @@ impl CoordinatorDB { }) } + /// used to check if the escrow locking transaction has been confirmed onchain pub async fn fetch_escrow_tx_confirmation_status(&self, offer_id: &str) -> Result { let status = sqlx::query("SELECT escrow_psbt_is_confirmed FROM taken_offers WHERE offer_id = ?") @@ -715,6 +678,7 @@ impl CoordinatorDB { Ok(status.get::("escrow_psbt_is_confirmed") == 1) } + /// used to set that a trader is satisfied with the trade (true) pub async fn set_trader_happy_field( &self, offer_id: &str, @@ -745,8 +709,8 @@ impl CoordinatorDB { Ok(()) } - // checked by the payout handler on request to determine if the trade is ready for payout and - // if escrow is required + /// checked by the payout handler on request to determine if the trade is ready for payout and + /// if escrow is required pub async fn fetch_trader_happiness(&self, offer_id: &str) -> Result { let row = sqlx::query( "SELECT maker_happy, taker_happy, escrow_ongoing FROM taken_offers WHERE offer_id = ?", @@ -766,7 +730,7 @@ impl CoordinatorDB { }) } - // this will be checked by the payout handler on request, the escrow winner will be set trough CLI input + /// this will be checked by the payout handler on request, the escrow winner will be set trough CLI input pub async fn fetch_escrow_result(&self, offer_id: &str) -> Result> { let row = sqlx::query("SELECT escrow_winner_robohash FROM taken_offers WHERE offer_id = ?") .bind(offer_id) @@ -779,6 +743,7 @@ impl CoordinatorDB { Ok(winner_robohash) } + /// fetch the amounts the traders have to contribute to the escrow locking transaction pub async fn get_escrow_tx_amounts( &self, trade_id: &str, @@ -808,6 +773,7 @@ impl CoordinatorDB { )) } + /// fetch the data required to construct the escrow psbt for the maker pub async fn fetch_maker_escrow_psbt_data( &self, trade_id: &str, @@ -830,6 +796,7 @@ impl CoordinatorDB { }) } + /// fetch the data required to construct the musig keyspend payout transaction to be signed by the traders on payout initialization pub async fn fetch_payout_data(&self, trade_id: &str) -> Result { let row = sqlx::query( "SELECT escrow_output_descriptor, payout_address_maker, @@ -866,6 +833,7 @@ impl CoordinatorDB { ) } + /// insert the keyspend payout transaction into the db pub async fn insert_keyspend_payout_psbt( &self, offer_id_hex: &str, @@ -879,6 +847,7 @@ impl CoordinatorDB { Ok(()) } + /// insert a partial signature submitted by the trader into the db pub async fn insert_partial_sig( &self, partial_sig_hex: &str, @@ -933,6 +902,7 @@ impl CoordinatorDB { Ok(()) } + /// fetches all data required to execute the keyspend payout (including the signatures) pub async fn fetch_keyspend_payout_information( &self, offer_id_hex: &str, @@ -971,6 +941,7 @@ impl CoordinatorDB { } } + /// fetches the keyspend payout psbt from the db pub async fn fetch_keyspend_payout_psbt(&self, offer_id_hex: &str) -> Result> { let row = sqlx::query("SELECT payout_transaction_psbt_hex FROM taken_offers WHERE offer_id = ?") @@ -982,6 +953,7 @@ impl CoordinatorDB { Ok(payout_psbt) } + /// used to as db lock to prevent race conditions when the payout is being handled pub async fn toggle_processing(&self, offer_id: &str) -> Result { let result = sqlx::query( r#" @@ -1002,6 +974,7 @@ impl CoordinatorDB { Ok(result.get::(0) == 1) } + /// deletes a finished offer from the database 🎉 pub async fn delete_complete_offer(&self, offer_id: &str) -> Result<()> { sqlx::query("DELETE FROM taken_offers WHERE offer_id = ?") .bind(offer_id) diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 8e680f0..c16d6da 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -69,6 +69,7 @@ use tokio::{ use validator::{Validate, ValidationError}; use wallet::{wallet_utils::*, *}; +// can be set false to disable logging in runtime (while awaiting cli input) static LOGGING_ENABLED: AtomicBool = AtomicBool::new(true); pub struct Coordinator { @@ -79,6 +80,7 @@ pub struct Coordinator { // populate .env with values before starting #[tokio::main] async fn main() -> Result<()> { + // Initialize the logger to write logs to stdout env_logger::builder() .filter_module("coordinator", log::LevelFilter::Trace) .filter_level(log::LevelFilter::Info) @@ -88,11 +90,10 @@ async fn main() -> Result<()> { let color_code = get_logging_color_code(level); writeln!( buf, - "{} [{}{}{}] - {}", + "{} [{}{}\x1B[0m] - {}", Local::now().format("%Y-%m-%d %H:%M:%S"), color_code, - level, - "\x1B[0m", // Reset color + level, // Reset color record.args() ) } else { @@ -130,6 +131,8 @@ async fn main() -> Result<()> { Ok(()) } +// get color code for logging based on log level +#[allow(dead_code)] fn get_logging_color_code(level: log::Level) -> &'static str { match level { log::Level::Error => "\x1B[31m", // Red