diff --git a/taptrade-cli-demo/coordinator/src/communication/api.rs b/taptrade-cli-demo/coordinator/src/communication/api.rs index a6f6885..94de63f 100644 --- a/taptrade-cli-demo/coordinator/src/communication/api.rs +++ b/taptrade-cli-demo/coordinator/src/communication/api.rs @@ -30,6 +30,8 @@ pub struct BondSubmissionRequest { pub taproot_pubkey_hex: String, pub musig_pub_nonce_hex: String, pub musig_pubkey_hex: String, + pub bdk_psbt_inputs_hex_csv: String, + pub client_change_address: String, } // Response after step2 if offer creation was successful and the offer is now online in the orderbook diff --git a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs index 2ab5971..4a25d71 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/create_taproot.rs @@ -338,11 +338,7 @@ fn taker_unresponsive( println!("here"); - wallet.add_signer( - KeychainKind::External, - SignerOrdering(0), - Arc::new(signer) - ); + 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")?; @@ -445,11 +441,7 @@ fn coordinator_sign( }, ); - wallet.add_signer( - KeychainKind::External, - SignerOrdering(0), - Arc::new(signer) - ); + 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()) { @@ -468,12 +460,11 @@ fn coordinator_sign( Ok(_) => { println!("Successfully signed PSBT."); println!("Final PSBT: {:?}", psbt); - }, + } Err(e) => { println!("Error signing PSBT: {:?}", e); } } - } Err(e) => { println!("Error creating wallet: {:?}", e); @@ -487,7 +478,7 @@ fn coordinator_sign( mod tests { use crate::coordinator; -use super::*; + use super::*; use anyhow::{Context, Error}; // use bdk::blockchain::ElectrumBlockchain; // use bdk::sled; diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index be2d0fe..f89c574 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -133,10 +133,7 @@ pub async fn handle_taker_bond( } debug!("\nTaker bond validation successful"); - let escrow_output_data = match wallet - .get_escrow_psbt_outputs(database, &payload.offer.offer_id_hex) - .await - { + let escrow_output_data = match wallet.get_escrow_psbt(database, &payload).await { Ok(escrow_output_data) => escrow_output_data, Err(e) => { return Err(BondError::CoordinatorError(e.to_string())); diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index fd48778..dad2684 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -100,6 +100,8 @@ impl CoordinatorDB { bond_amount_sat INTEGER NOT NULL, bond_tx_hex TEXT NOT NULL, payout_address TEXT NOT NULL, + change_address_maker TEXT NOT NULL, + escrow_inputs_hex_maker_csv TEXT NOT NULL, taproot_pubkey_hex_maker TEXT NOT NULL, musig_pub_nonce_hex TEXT NOT NULL, musig_pubkey_hex TEXT NOT NULL, @@ -241,8 +243,9 @@ impl CoordinatorDB { ); sqlx::query( "INSERT OR REPLACE INTO active_maker_offers (offer_id, robohash, is_buy_order, amount_sat, - bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, bond_tx_hex, payout_address, taproot_pubkey_hex_maker, musig_pub_nonce_hex, musig_pubkey_hex, taker_bond_address) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, bond_tx_hex, payout_address, taproot_pubkey_hex_maker, musig_pub_nonce_hex, musig_pubkey_hex, taker_bond_address, + change_address_maker, escrow_inputs_hex_maker_csv) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(offer_id) .bind(hex::decode(&data.robohash_hex)?) @@ -258,6 +261,8 @@ impl CoordinatorDB { .bind(data.musig_pub_nonce_hex.clone()) .bind(data.musig_pubkey_hex.clone()) .bind(taker_bond_address) + .bind(data.client_change_address.clone()) + .bind(data.bdk_psbt_inputs_hex_csv.clone()) .execute(&*self.db_pool) .await?; diff --git a/taptrade-cli-demo/coordinator/src/wallet/mod.rs b/taptrade-cli-demo/coordinator/src/wallet/mod.rs index 34eb0ca..47ca8f4 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/mod.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/mod.rs @@ -25,6 +25,7 @@ use bdk::{ KeychainKind, SyncOptions, Wallet, }; use coordinator::mempool_monitoring::MempoolHandler; +use core::panic; use std::{collections::HashMap, str::FromStr}; use std::{fmt, ops::Deref}; use utils::*; @@ -236,11 +237,13 @@ impl CoordinatorWallet { Ok(()) } - pub async fn get_escrow_psbt_outputs( + pub async fn get_escrow_psbt( &self, db: &Arc, - trade_id: &str, + taker_psbt_request: &OfferPsbtRequest, ) -> Result { + let trade_id = &taker_psbt_request.offer.offer_id_hex; + panic!("adjust"); let escrow_pubkeys = db.fetch_escrow_tx_payout_data(trade_id).await?; let coordinator_escrow_pk = self.get_coordinator_taproot_pk().await?; let escrow_output_descriptor = diff --git a/taptrade-cli-demo/trader/Cargo.lock b/taptrade-cli-demo/trader/Cargo.lock index bf4ab72..acb7dad 100644 --- a/taptrade-cli-demo/trader/Cargo.lock +++ b/taptrade-cli-demo/trader/Cargo.lock @@ -175,6 +175,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitcoin" version = "0.30.2" @@ -1587,6 +1596,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bdk", + "bincode", "dotenvy", "env_logger", "hex", diff --git a/taptrade-cli-demo/trader/Cargo.toml b/taptrade-cli-demo/trader/Cargo.toml index 9444410..fe6f45e 100644 --- a/taptrade-cli-demo/trader/Cargo.toml +++ b/taptrade-cli-demo/trader/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0.86" bdk = "0.29.0" +bincode = "1.3.3" dotenvy = "0.15.0" env_logger = "0.11.3" hex = "0.4.3" diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 95db5e3..7c89ce6 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -29,6 +29,8 @@ pub struct BondSubmissionRequest { pub taproot_pubkey_hex: String, pub musig_pub_nonce_hex: String, pub musig_pubkey_hex: String, + pub bdk_psbt_inputs_hex_csv: String, + pub client_change_address: String, } // Response after step2 if offer creation was successful and the offer is now online in the orderbook diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index c207039..37ac233 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use std::{f32::consts::E, str::FromStr, thread::sleep, time::Duration}; impl BondRequirementResponse { - fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { + fn _format_order_request(trader_setup: &TraderSettings) -> OrderRequest { let amount: u64; let is_buy_order = match &trader_setup.trade_type { OfferType::Buy(val) => { @@ -46,7 +46,7 @@ impl BondRequirementResponse { let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer"); let res = match client .post(endpoint) - .json(&Self::_format_request(trader_setup)) + .json(&Self::_format_order_request(trader_setup)) .send() { Ok(res) => res, diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index cbb6e11..7253eaf 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -10,6 +10,10 @@ impl ActiveOffer { debug!("Offer conditions fetched: {:#?}", &offer_conditions); let (bond, mut musig_data, payout_address) = trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?; + + let (psbt_inputs_hex_csv, escrow_change_address) = + trading_wallet.get_escrow_psbt_inputs()?; + let submission_result = BondSubmissionRequest::send_maker( &maker_config.robosats_robohash_hex, &bond, diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 6057fde..393583f 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -13,7 +13,7 @@ use bdk::{ self, bip32::ExtendedPrivKey, key::{KeyPair, Secp256k1, XOnlyPublicKey}, - psbt::PartiallySignedTransaction, + psbt::{serialize, Input, PartiallySignedTransaction}, Address, Network, }, blockchain::ElectrumBlockchain, @@ -28,6 +28,7 @@ use bdk::{ use bond::Bond; use cli::OfferType; use musig2::MuSigData; +use serde::Serialize; use std::str::FromStr; use wallet_utils::get_seed; @@ -89,43 +90,69 @@ impl TradingWallet { Ok((bond, musig_data, payout_address)) } - pub fn get_escrow_psbt( - &self, - escrow_psbt_requirements: OfferTakenResponse, - trader_config: &TraderSettings, - ) -> Result { - let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)? - .assume_checked() - .script_pubkey(); - let escrow_output = { - let temp_wallet = Wallet::new( - &escrow_psbt_requirements.escrow_output_descriptor, - None, - Network::Regtest, - MemoryDatabase::new(), - )?; - temp_wallet.get_address(AddressIndex::New)?.script_pubkey() - }; - self.wallet.sync(&self.backend, SyncOptions::default())?; + // pub fn get_escrow_psbt( + // &self, + // escrow_psbt_requirements: OfferTakenResponse, + // trader_config: &TraderSettings, + // ) -> Result { + // let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)? + // .assume_checked() + // .script_pubkey(); + // let escrow_output = { + // let temp_wallet = Wallet::new( + // &escrow_psbt_requirements.escrow_output_descriptor, + // None, + // Network::Regtest, + // MemoryDatabase::new(), + // )?; + // temp_wallet.get_address(AddressIndex::New)?.script_pubkey() + // }; + // self.wallet.sync(&self.backend, SyncOptions::default())?; - let escrow_amount_sat = match trader_config.trade_type { - OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat, - OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat, - }; - let (mut psbt, details) = { - let mut builder = self.wallet.build_tx(); - builder - .add_recipient(escrow_output, escrow_amount_sat) - .add_recipient( - fee_output, - escrow_psbt_requirements.escrow_fee_sat_per_participant, - ) - .fee_rate(FeeRate::from_sat_per_vb(10.0)); - builder.finish()? - }; - debug!("Signing escrow psbt."); - self.wallet.sign(&mut psbt, SignOptions::default())?; - Ok(psbt) + // let escrow_amount_sat = match trader_config.trade_type { + // OfferType::Buy(_) => escrow_psbt_requirements.escrow_amount_taker_sat, + // OfferType::Sell(_) => escrow_psbt_requirements.escrow_amount_maker_sat, + // }; + // let (mut psbt, details) = { + // let mut builder = self.wallet.build_tx(); + // builder + // .add_recipient(escrow_output, escrow_amount_sat) + // .add_recipient( + // fee_output, + // escrow_psbt_requirements.escrow_fee_sat_per_participant, + // ) + // .fee_rate(FeeRate::from_sat_per_vb(10.0)); + // builder.finish()? + // }; + // debug!("Signing escrow psbt."); + // self.wallet.sign(&mut psbt, SignOptions::default())?; + // Ok(psbt) + // } + + /// returns suitable inputs (hex, csv serialized) and a change address for the assembly of the escrow psbt (coordinator side) + pub fn get_escrow_psbt_inputs(&self, mut amount_sat: i64) -> Result<(String, String)> { + let mut inputs: Vec = Vec::new(); + + self.wallet.sync(&self.backend, SyncOptions::default())?; + let available_utxos = self.wallet.list_unspent()?; + + // could use more advanced coin selection if neccessary + for utxo in available_utxos { + let psbt_input = self.wallet.get_psbt_input(utxo, None, false)?; + inputs.push(hex::encode(bincode::serialize(&psbt_input)?)); + amount_sat -= utxo.txout.value as i64; + if amount_sat <= 0 { + break; + } + } + let serialized_inputs = inputs.join(","); + + let change_address = self + .wallet + .get_address(AddressIndex::New)? + .address + .to_string(); + Ok((serialized_inputs, change_address)) } // validate that the taker psbt references the correct inputs and amounts