diff --git a/taptrade-cli-demo/coordinator/.env b/taptrade-cli-demo/coordinator/.env index 8b8f2e7..fcabaf9 100644 --- a/taptrade-cli-demo/coordinator/.env +++ b/taptrade-cli-demo/coordinator/.env @@ -6,3 +6,4 @@ BDK_DB_PATH="./dbs/bdk-wallet" # Path to the BDK Sled database (no .db postfix) WALLET_XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" PUNISHMENT_ENABLED=1 # enable punishment for misbehaving traders PORT=9999 # port for the coordinator to listen on +COORDINATOR_FEERATE=1 # coordinator fee in percent of the trade amount diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index 4958afe..be2d0fe 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -144,12 +144,7 @@ pub async fn handle_taker_bond( }; if let Err(e) = database - .add_taker_info_and_move_table( - payload, - &escrow_output_data.escrow_output_descriptor, - &escrow_output_data.escrow_tx_fee_address, - &escrow_output_data.coordinator_xonly_escrow_pk, - ) + .add_taker_info_and_move_table(payload, &escrow_output_data) .await { return Err(BondError::CoordinatorError(e.to_string())); @@ -158,6 +153,9 @@ pub async fn handle_taker_bond( Ok(OfferTakenResponse { escrow_output_descriptor: escrow_output_data.escrow_output_descriptor, escrow_tx_fee_address: escrow_output_data.escrow_tx_fee_address, + escrow_amount_maker_sat: escrow_output_data.escrow_amount_maker_sat, + escrow_amount_taker_sat: escrow_output_data.escrow_amount_taker_sat, + escrow_fee_sat_per_participant: escrow_output_data.escrow_fee_sat_per_participant, }) } @@ -167,11 +165,18 @@ pub async fn get_offer_status_maker( ) -> Result { let database = &coordinator.coordinator_db; - let (descriptor, fee_address) = match database + let EscrowPsbt { + escrow_output_descriptor, + escrow_tx_fee_address, + escrow_amount_maker_sat, + escrow_amount_taker_sat, + escrow_fee_sat_per_participant, + .. + } = match database .fetch_escrow_output_information(&payload.offer_id_hex) .await { - Ok(Some(descriptor_and_fee_addr)) => (descriptor_and_fee_addr.0, descriptor_and_fee_addr.1), + Ok(Some(escrow_psbt_data)) => escrow_psbt_data, Ok(None) => { return Err(FetchOffersError::NoOffersAvailable); } @@ -180,8 +185,11 @@ pub async fn get_offer_status_maker( } }; Ok(OfferTakenResponse { - escrow_output_descriptor: descriptor, - escrow_tx_fee_address: fee_address, + escrow_output_descriptor, + escrow_tx_fee_address, + escrow_amount_maker_sat, + escrow_amount_taker_sat, + escrow_fee_sat_per_participant, }) } diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index 0202669..fd48778 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -139,7 +139,11 @@ impl CoordinatorDB { taker_happy INTEGER, escrow_ongoing INTEGER NOT NULL, escrow_winner_robohash TEXT, - escrow_taproot_pk_coordinator TEXT + escrow_taproot_pk_coordinator TEXT, + escrow_amount_maker_sat INTEGER, + escrow_amount_taker_sat INTEGER, + escrow_fee_per_participant INTEGER, + escrow_output_descriptor TEXT )", // escrow_psbt_is_confirmed will be set 1 once the escrow psbt is confirmed onchain ) .execute(&db_pool) @@ -353,9 +357,7 @@ impl CoordinatorDB { pub async fn add_taker_info_and_move_table( &self, trade_and_taker_info: &OfferPsbtRequest, - escrow_output_descriptor: &str, - escrow_tx_fee_address: &str, - escrow_taproot_pk_coordinator: &str, + escrow_tx_data: &EscrowPsbt, ) -> Result<()> { let public_offer = self .fetch_and_delete_offer_from_public_offers_table( @@ -368,8 +370,8 @@ impl CoordinatorDB { bond_ratio, offer_duration_ts, bond_address_maker, bond_address_taker, bond_amount_sat, bond_tx_hex_maker, bond_tx_hex_taker, payout_address_maker, payout_address_taker, taproot_pubkey_hex_maker, taproot_pubkey_hex_taker, musig_pub_nonce_hex_maker, musig_pubkey_hex_maker, musig_pub_nonce_hex_taker, musig_pubkey_hex_taker, escrow_output_descriptor, escrow_tx_fee_address, escrow_psbt_is_confirmed, escrow_ongoing, - escrow_taproot_pk_coordinator) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + escrow_taproot_pk_coordinator, escrow_amount_maker_sat, escrow_amount_taker_sat, escrow_fee_per_participant) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(public_offer.offer_id) .bind(public_offer.robohash_maker) @@ -391,11 +393,14 @@ impl CoordinatorDB { .bind(public_offer.musig_pubkey_hex_maker) .bind(trade_and_taker_info.trade_data.musig_pub_nonce_hex.clone()) .bind(trade_and_taker_info.trade_data.musig_pubkey_hex.clone()) - .bind(escrow_output_descriptor) - .bind(escrow_tx_fee_address) + .bind(&escrow_tx_data.escrow_output_descriptor) + .bind(&escrow_tx_data.escrow_tx_fee_address) .bind(0) .bind(0) - .bind(escrow_taproot_pk_coordinator) + .bind(&escrow_tx_data.coordinator_xonly_escrow_pk) + .bind(escrow_tx_data.escrow_amount_maker_sat as i64) + .bind(escrow_tx_data.escrow_amount_taker_sat as i64) + .bind(escrow_tx_data.escrow_fee_sat_per_participant as i64) .execute(&*self.db_pool) .await?; @@ -405,9 +410,11 @@ impl CoordinatorDB { pub async fn fetch_escrow_output_information( &self, offer_id_hex: &str, - ) -> Result> { + ) -> Result> { let offer = sqlx::query( - "SELECT escrow_output_descriptor, escrow_tx_fee_address FROM taken_offers WHERE offer_id = ?", + "SELECT escrow_output_descriptor, escrow_tx_fee_address, escrow_amount_maker_sat, + escrow_amount_taker_sat, escrow_fee_per_participant, escrow_taproot_pk_coordinator + FROM taken_offers WHERE offer_id = ?", ) .bind(offer_id_hex) .fetch_optional(&*self.db_pool) @@ -416,9 +423,23 @@ impl CoordinatorDB { Some(offer) => offer, None => return Ok(None), }; - let descriptor = offer.try_get::("escrow_output_descriptor")?; - let fee_address = offer.try_get::("escrow_tx_fee_address")?; - Ok(Some((descriptor, fee_address))) + let escrow_output_descriptor = offer.try_get::("escrow_output_descriptor")?; + let escrow_tx_fee_address = offer.try_get::("escrow_tx_fee_address")?; + let escrow_amount_maker_sat = offer.try_get::("escrow_amount_maker_sat")? as u64; + let escrow_amount_taker_sat = offer.try_get::("escrow_amount_taker_sat")? as u64; + let escrow_fee_sat_per_participant = + offer.try_get::("escrow_fee_per_participant")? as u64; + let coordinator_xonly_escrow_pk = + offer.try_get::("escrow_taproot_pk_coordinator")?; + + Ok(Some(EscrowPsbt { + escrow_output_descriptor, + escrow_tx_fee_address, + coordinator_xonly_escrow_pk, + escrow_amount_maker_sat, + escrow_amount_taker_sat, + escrow_fee_sat_per_participant, + })) } // returns a hashmap of RoboHash, MonitoringBond for the monitoring loop @@ -689,4 +710,32 @@ impl CoordinatorDB { musig_pubkey_compressed_hex_taker, }) } + + pub async fn get_escrow_tx_amounts( + &self, + trade_id: &str, + coordinator_feerate: f64, + ) -> Result<(u64, u64, u64)> { + let row = sqlx::query( + "SELECT amount_sat, is_buy_order, bond_amount_sat FROM active_maker_offers WHERE offer_id = ?", + ).bind(trade_id).fetch_one(&*self.db_pool).await?; + + let amount_sat: u64 = row.get("amount_sat"); + let is_buy_order: bool = 1 == row.get::("is_buy_order"); + let bond_amount_sat: u64 = row.get("bond_amount_sat"); + + let escrow_fee_per_participant: u64 = (amount_sat as f64 * coordinator_feerate) as u64; + + let (escrow_amount_maker_sat, escrow_amount_taker_sat) = if is_buy_order { + (amount_sat + bond_amount_sat, bond_amount_sat) + } else { + (bond_amount_sat, amount_sat + bond_amount_sat) + }; + + Ok(( + escrow_amount_maker_sat, + escrow_amount_taker_sat, + escrow_fee_per_participant, + )) + } } diff --git a/taptrade-cli-demo/coordinator/src/wallet/mod.rs b/taptrade-cli-demo/coordinator/src/wallet/mod.rs index e35d2ce..34eb0ca 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/mod.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/mod.rs @@ -36,6 +36,7 @@ pub struct CoordinatorWallet { pub backend: Arc, pub json_rpc_client: Arc, pub mempool: Arc, + pub coordinator_feerate: f64, } #[derive(Debug)] @@ -43,6 +44,9 @@ pub struct EscrowPsbt { pub escrow_output_descriptor: String, pub escrow_tx_fee_address: String, pub coordinator_xonly_escrow_pk: String, + pub escrow_amount_maker_sat: u64, + pub escrow_amount_taker_sat: u64, + pub escrow_fee_sat_per_participant: u64, } #[derive(PartialEq, Debug, Clone)] @@ -98,6 +102,7 @@ pub async fn init_coordinator_wallet() -> Result> backend: Arc::new(backend), json_rpc_client, mempool: Arc::new(mempool), + coordinator_feerate: env::var("COORDINATOR_FEERATE")?.parse::()?, }) } @@ -241,11 +246,17 @@ impl CoordinatorWallet { let escrow_output_descriptor = build_escrow_transaction_output_descriptor(&escrow_pubkeys, &coordinator_escrow_pk)?; let escrow_tx_fee_address = self.get_new_address().await?; + let (escrow_amount_maker_sat, escrow_amount_taker_sat, escrow_fee_sat_per_participant) = db + .get_escrow_tx_amounts(trade_id, self.coordinator_feerate) + .await?; Ok(EscrowPsbt { escrow_output_descriptor, escrow_tx_fee_address, coordinator_xonly_escrow_pk: coordinator_escrow_pk.to_string(), + escrow_amount_maker_sat, + escrow_amount_taker_sat, + escrow_fee_sat_per_participant, }) } diff --git a/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs b/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs index 7e1853b..e1d4dec 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/wallet_tests.rs @@ -45,6 +45,7 @@ async fn new_test_wallet(wallet_xprv: &str) -> CoordinatorWallet backend: Arc::new(backend), json_rpc_client: Arc::clone(&json_rpc_client), mempool: Arc::new(MempoolHandler::new(json_rpc_client).await), + coordinator_feerate: env::var("COORDINATOR_FEERATE").unwrap().parse().unwrap(), } } diff --git a/taptrade-cli-demo/trader/src/communication/taker_requests.rs b/taptrade-cli-demo/trader/src/communication/taker_requests.rs index fa6b264..9ef2fff 100644 --- a/taptrade-cli-demo/trader/src/communication/taker_requests.rs +++ b/taptrade-cli-demo/trader/src/communication/taker_requests.rs @@ -65,7 +65,7 @@ impl OfferPsbtRequest { offer: &PublicOffer, trade_data: BondSubmissionRequest, taker_config: &TraderSettings, - ) -> Result { + ) -> Result { let request = OfferPsbtRequest { offer: offer.clone(), trade_data, @@ -82,8 +82,6 @@ impl OfferPsbtRequest { .json::()?; debug!("Trader received escrow psbt"); - let psbt_bytes = hex::decode(res.trade_psbt_hex_to_sign)?; - let psbt = PartiallySignedTransaction::deserialize(&psbt_bytes)?; - Ok(psbt) + Ok(res) } } diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 203717f..2f9af23 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -31,13 +31,13 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { info!("Maker offer created: {:#?}", &offer); let escrow_psbt_requirements = offer.wait_until_taken(maker_config)?; - let escrow_psbt = wallet.get_escrow_psbt(escrow_psbt_requirements, maker_config); + let escrow_psbt = wallet.get_escrow_psbt(escrow_psbt_requirements, maker_config)?; // .validate_maker_psbt(&escrow_contract_psbt)? // .sign_escrow_psbt(&mut escrow_contract_psbt)?; // submit signed escrow psbt back to coordinator PsbtSubmissionRequest::submit_escrow_psbt( - &escrow_contract_psbt, + &escrow_psbt, offer.offer_id_hex.clone(), maker_config, )?; diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 1d94dd1..bed34c9 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -29,13 +29,12 @@ impl ActiveOffer { taker_config, &trading_wallet.taproot_pubkey, )?; - let mut escrow_contract_psbt = + let escrow_contract_requirements = OfferPsbtRequest::taker_request(offer, bond_submission_request, taker_config)?; // now we have to verify, sign and submit the escrow psbt again - trading_wallet - .validate_taker_psbt(&escrow_contract_psbt)? - .sign_escrow_psbt(&mut escrow_contract_psbt)?; + let escrow_contract_psbt = + trading_wallet.get_escrow_psbt(escrow_contract_requirements, taker_config)?; // submit signed escrow psbt back to coordinator PsbtSubmissionRequest::submit_escrow_psbt( diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 2f29bf7..6057fde 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -26,6 +26,7 @@ use bdk::{ FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet, }; use bond::Bond; +use cli::OfferType; use musig2::MuSigData; use std::str::FromStr; use wallet_utils::get_seed; @@ -88,12 +89,10 @@ impl TradingWallet { Ok((bond, musig_data, payout_address)) } - pub async fn get_escrow_psbt( + pub fn get_escrow_psbt( &self, escrow_psbt_requirements: OfferTakenResponse, trader_config: &TraderSettings, - escrow_amount: u64, - coordinator_fee_amount: u64, ) -> Result { let fee_output = Address::from_str(&escrow_psbt_requirements.escrow_tx_fee_address)? .assume_checked() @@ -108,11 +107,19 @@ impl TradingWallet { 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) - .add_recipient(fee_output, coordinator_fee_amount) + .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()? }; @@ -124,11 +131,11 @@ impl TradingWallet { // validate that the taker psbt references the correct inputs and amounts // taker input should be the same as in the previous bond transaction. // input amount should be the bond amount when buying, - pub fn validate_taker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> { - error!("IMPLEMENT TAKER PSBT VALIDATION!"); - // tbd once the trade psbt is implemented on coordinator side - Ok(self) - } + // pub fn validate_taker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> { + // error!("IMPLEMENT TAKER PSBT VALIDATION!"); + // // tbd once the trade psbt is implemented on coordinator side + // Ok(self) + // } // pub fn sign_escrow_psbt(&self, escrow_psbt: &mut PartiallySignedTransaction) -> Result<&Self> { // let finalized = self.wallet.sign(escrow_psbt, SignOptions::default())?;