diff --git a/taptrade-cli-demo/coordinator/Cargo.toml b/taptrade-cli-demo/coordinator/Cargo.toml index 0aa1102..ac1515e 100644 --- a/taptrade-cli-demo/coordinator/Cargo.toml +++ b/taptrade-cli-demo/coordinator/Cargo.toml @@ -35,6 +35,6 @@ lto = true opt-level = 3 strip = true -[lib] -name = "coordinator" -path = "src/main.rs" +# [lib] +# name = "coordinator" +# path = "src/main.rs" diff --git a/taptrade-cli-demo/coordinator/src/communication/api.rs b/taptrade-cli-demo/coordinator/src/communication/api.rs index 7c75b03..af6d12b 100644 --- a/taptrade-cli-demo/coordinator/src/communication/api.rs +++ b/taptrade-cli-demo/coordinator/src/communication/api.rs @@ -86,10 +86,11 @@ pub struct OfferRequest { pub offer_duration_ts: u64, // unix timestamp how long the offer should stay available } -#[derive(Serialize, PartialEq, Debug, Validate)] +#[derive(Serialize, PartialEq, Debug, Validate, Deserialize)] pub struct BondRequirementResponse { pub bond_address: String, pub locking_amount_sat: u64, // min amount of the bond output in sat + pub escrow_locking_input_amount_without_trade_sum: u64, } // maker step 2 @@ -124,8 +125,7 @@ pub struct OffersRequest { pub struct PublicOffer { pub amount_sat: u64, pub offer_id_hex: String, - pub required_bond_amount_sat: u64, - pub bond_locking_address: String, + pub bond_requirements: BondRequirementResponse, } #[derive(Deserialize, Serialize, Debug)] diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index 872da6b..e691594 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -15,31 +15,23 @@ pub async fn process_order( let wallet = &coordinator.coordinator_wallet; let database = &coordinator.coordinator_db; - // the client also uses this amount to select inputs for the escrow psbt, this could be separated - // to make the required bond amount lower without causing the client to return too small inputs - // 5000 is the abs tx fee used in the escrow psbt per trader - panic!("This is borked"); - let coordinator_feerate = (coordinator.coordinator_wallet.coordinator_feerate - * offer.amount_satoshi as f64) as u64 - / 100; - let locking_amount_sat = match offer.is_buy_order { - true => { - 5000 + coordinator_feerate + offer.amount_satoshi * u64::from(offer.bond_ratio) / 100 - } - false => { - (offer.amount_satoshi * u64::from(offer.bond_ratio) / 100) - + offer.amount_satoshi - + 5000 + coordinator_feerate - } - }; + let bond_amount = (offer.bond_ratio as u64 * offer.amount_satoshi) / 100; + let coordinator_fee = ((coordinator.coordinator_wallet.coordinator_feerate + * offer.amount_satoshi as f64) + / 100.0) as u64; + let absolute_tx_fee = 5000; + // 5000 is a buffer + let escrow_locking_input_amount_without_trade_sum = + bond_amount + coordinator_fee + absolute_tx_fee + 5000; trace!( "Offer amount: {}, Locking amount: {}", offer.amount_satoshi, - locking_amount_sat + bond_amount ); let bond_requirements = BondRequirementResponse { bond_address: wallet.get_new_address().await?, - locking_amount_sat, + locking_amount_sat: bond_amount, + escrow_locking_input_amount_without_trade_sum, }; database diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index 4c772cf..aceeb21 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -18,6 +18,7 @@ struct AwaitingBondOffer { offer_duration_ts: u64, bond_address: String, bond_amount_sat: u64, + escrow_locking_input_amount_without_trade_sum: u64, } #[derive(PartialEq, Debug)] @@ -78,7 +79,8 @@ impl CoordinatorDB { bond_ratio INTEGER NOT NULL, offer_duration_ts INTEGER NOT NULL, bond_address TEXT NOT NULL, - bond_amount_sat INTEGER NOT NULL + bond_amount_sat INTEGER NOT NULL, + escrow_locking_input_amount_without_trade_sum INTEGER NOT NULL )", ) .execute(&db_pool) @@ -96,6 +98,7 @@ impl CoordinatorDB { offer_duration_ts INTEGER NOT NULL, bond_address TEXT NOT NULL, bond_amount_sat INTEGER NOT NULL, + escrow_locking_input_amount_without_trade_sum INTEGER, bond_tx_hex TEXT NOT NULL, payout_address TEXT NOT NULL, change_address_maker TEXT NOT NULL, @@ -169,8 +172,8 @@ impl CoordinatorDB { ) -> Result<()> { sqlx::query( "INSERT OR REPLACE INTO maker_requests (robohash, is_buy_order, amount_sat, - bond_ratio, offer_duration_ts, bond_address, bond_amount_sat) - VALUES (?, ?, ?, ?, ?, ?, ?)", + bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, escrow_locking_input_amount_without_trade_sum) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(hex::decode(&order.robohash_hex)?) .bind(bool_to_sql_int(order.is_buy_order)) @@ -179,6 +182,7 @@ impl CoordinatorDB { .bind(order.offer_duration_ts as i64) .bind(bond_requirements.bond_address.clone()) .bind(bond_requirements.locking_amount_sat as i64) + .bind(bond_requirements.escrow_locking_input_amount_without_trade_sum as i64) .execute(&*self.db_pool) .await?; @@ -207,7 +211,7 @@ impl CoordinatorDB { robohash_hex: &str, ) -> Result { let fetched_values = sqlx::query_as::<_, (Vec, bool, i64, u8, i64, String, i64)> ( - "SELECT robohash, is_buy_order, amount_sat, bond_ratio, offer_duration_ts, bond_address, bond_amount_sat FROM maker_requests WHERE robohash = ?", + "SELECT robohash, is_buy_order, amount_sat, bond_ratio, offer_duration_ts, bond_address, bond_amount_sat, escrow_locking_input_amount_without_trade_sum FROM maker_requests WHERE robohash = ?", ) .bind(hex::decode(robohash_hex)?) .fetch_one(&*self.db_pool) @@ -226,6 +230,7 @@ impl CoordinatorDB { offer_duration_ts: fetched_values.4 as u64, bond_address: fetched_values.5, bond_amount_sat: fetched_values.6 as u64, + escrow_locking_input_amount_without_trade_sum: fetched_values.6 as u64, }; debug!( "Deleted offer from maker_requests table. Fetched offer: {:#?}", @@ -252,8 +257,8 @@ 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, - change_address_maker, escrow_inputs_hex_maker_csv) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + change_address_maker, escrow_inputs_hex_maker_csv, escrow_locking_input_amount_without_trade_sum) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(offer_id) .bind(hex::decode(&data.robohash_hex)?) @@ -271,6 +276,7 @@ impl CoordinatorDB { .bind(taker_bond_address) .bind(data.client_change_address.clone()) .bind(data.bdk_psbt_inputs_hex_csv.clone()) + .bind(remaining_offer_information.escrow_locking_input_amount_without_trade_sum as i64) .execute(&*self.db_pool) .await?; @@ -287,8 +293,8 @@ impl CoordinatorDB { "Fetching suitable offers from db. Specification: {:#?}", requested_offer ); - let fetched_offers = sqlx::query_as::<_, (String, i64, i64, String)> ( - "SELECT offer_id, amount_sat, bond_amount_sat, taker_bond_address FROM active_maker_offers WHERE is_buy_order = ? AND amount_sat BETWEEN ? AND ?", + let fetched_offers = sqlx::query_as::<_, (String, i64, i64, String, i64)> ( + "SELECT offer_id, amount_sat, bond_amount_sat, taker_bond_address, escrow_locking_input_amount_without_trade_sum FROM active_maker_offers WHERE is_buy_order = ? AND amount_sat BETWEEN ? AND ?", ) .bind(requested_offer.buy_offers) .bind(requested_offer.amount_min_sat as i64) @@ -299,11 +305,16 @@ impl CoordinatorDB { let available_offers: Vec = fetched_offers .into_iter() .map( - |(offer_id_hex, amount_sat, bond_amount_sat, bond_address_taker)| PublicOffer { - offer_id_hex, - amount_sat: amount_sat as u64, - required_bond_amount_sat: bond_amount_sat as u64, - bond_locking_address: bond_address_taker, + |(offer_id_hex, amount_sat, bond_amount_sat, bond_address_taker, min_inputs)| { + PublicOffer { + offer_id_hex, + amount_sat: amount_sat as u64, + bond_requirements: BondRequirementResponse { + bond_address: bond_address_taker, + locking_amount_sat: bond_amount_sat as u64, + escrow_locking_input_amount_without_trade_sum: min_inputs as u64, + }, + } }, ) .collect(); @@ -400,7 +411,7 @@ impl CoordinatorDB { .bind(public_offer.bond_ratio) .bind(public_offer.offer_duration_ts) .bind(public_offer.bond_address_maker) - .bind(trade_and_taker_info.offer.bond_locking_address.clone()) + .bind(trade_and_taker_info.offer.bond_requirements.bond_address.clone()) .bind(public_offer.bond_amount_sat) .bind(public_offer.bond_tx_hex_maker) .bind(trade_and_taker_info.trade_data.signed_bond_hex.clone()) diff --git a/taptrade-cli-demo/trader/Cargo.toml b/taptrade-cli-demo/trader/Cargo.toml index fe6f45e..a13b7d7 100644 --- a/taptrade-cli-demo/trader/Cargo.toml +++ b/taptrade-cli-demo/trader/Cargo.toml @@ -13,8 +13,8 @@ hex = "0.4.3" log = "0.4.21" musig2 = "0.0.11" rand_core = "0.6.4" -reqwest = { version = "0.12.4", features = ["blocking", "json"] } -serde = "1.0.203" +reqwest = { version = "0.12", features = ["blocking", "json"] } +serde = "1.0" sha2 = "0.10.8" [profile.release] diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 834e8f1..14b20da 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -13,10 +13,11 @@ pub struct OrderRequest { // coordinator answer to maker step 1 // direct Json answer to step 1 (same request) -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct BondRequirementResponse { pub bond_address: String, // address the bond ha/workspaces/taptrade-core/taptrade-cli-demo/trader/src/communications to be locked to pub locking_amount_sat: u64, // min amount of the bond output in sat + pub escrow_locking_input_amount_without_trade_sum: u64, // minimum required amount of input to the escrow tx } // maker step 2 @@ -76,8 +77,7 @@ pub struct PublicOffers { pub struct PublicOffer { pub amount_sat: u64, pub offer_id_hex: String, - pub required_bond_amount_sat: u64, - pub bond_locking_address: String, // its probably bad privacy to make the locking address static + pub bond_requirements: BondRequirementResponse, } // request to receive the escrow psbt to sign for the specified offer to take it diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index 4c4a6c5..5a98811 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -20,8 +20,14 @@ impl ActiveOffer { trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?; // get necessary data for the coordinator to assemble the escrow locking psbt (inputs owned by maker, change address) + let input_amount = if maker_config.trade_type.is_buy_order() { + offer_conditions.escrow_locking_input_amount_without_trade_sum + } else { + offer_conditions.escrow_locking_input_amount_without_trade_sum + + maker_config.trade_type.value() + }; let (psbt_inputs_hex_csv, escrow_change_address) = - trading_wallet.get_escrow_psbt_inputs(offer_conditions.locking_amount_sat as i64)?; + trading_wallet.get_escrow_psbt_inputs(input_amount)?; debug!( "Submitting maker bond: {:#?}", diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index f96a886..f143e90 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -69,7 +69,7 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { )?; // submit signed payout psbt back to coordinator PayoutSignatureRequest::send(maker_config, &signature, &offer.offer_id_hex)?; - // now the coordinator will broadcast the payout transaction and the trade should be finished + debug!("now the coordinator will broadcast the payout transaction and the trade should be finished"); } else { warn!("Trader unsatisfied. Initiating escrow mode."); TradeObligationsUnsatisfied::request_escrow(&offer.offer_id_hex, maker_config)?; @@ -122,7 +122,7 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { // submit partial signature back to coordinator PayoutSignatureRequest::send(taker_config, &signature, &accepted_offer.offer_id_hex)?; - // now the coordinator will broadcast the payout transaction and the trade should be finished + debug!("now the coordinator will broadcast the payout transaction and the trade should be finished"); // here we need to handle if the other party is not cooperating } else { error!("Trader unsatisfied. Initiating escrow mode."); diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 7817f7e..33ff0a7 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -12,18 +12,23 @@ impl ActiveOffer { taker_config: &TraderSettings, offer: &PublicOffer, ) -> Result { - let bond_requirements = BondRequirementResponse { - bond_address: offer.bond_locking_address.clone(), - locking_amount_sat: offer.required_bond_amount_sat, - }; - // assembly of the Bond transaction and generation of MuSig data and payout address let (bond, mut musig_data, payout_address) = - trading_wallet.trade_onchain_assembly(&bond_requirements, taker_config)?; + trading_wallet.trade_onchain_assembly(&offer.bond_requirements, taker_config)?; // get inputs and a change address necessary for the coordinator to assemble the escrow locking psbt + let input_amount = if taker_config.trade_type.is_buy_order() { + offer + .bond_requirements + .escrow_locking_input_amount_without_trade_sum + } else { + offer + .bond_requirements + .escrow_locking_input_amount_without_trade_sum + + taker_config.trade_type.value() + }; let (bdk_psbt_inputs_hex_csv, client_change_address) = - trading_wallet.get_escrow_psbt_inputs(bond_requirements.locking_amount_sat as i64)?; + trading_wallet.get_escrow_psbt_inputs(input_amount)?; let bond_submission_request = BondSubmissionRequest { robohash_hex: taker_config.robosats_robohash_hex.clone(), diff --git a/taptrade-cli-demo/trader/src/wallet/bond.rs b/taptrade-cli-demo/trader/src/wallet/bond.rs index e052cdd..8c922dd 100644 --- a/taptrade-cli-demo/trader/src/wallet/bond.rs +++ b/taptrade-cli-demo/trader/src/wallet/bond.rs @@ -99,6 +99,7 @@ mod tests { let bond_target = BondRequirementResponse { bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), locking_amount_sat: 10000, + escrow_locking_input_amount_without_trade_sum: 10000, }; let trader_input = TraderSettings { electrum_endpoint: "ssl://mempool.space:40002".to_string(), @@ -123,6 +124,7 @@ mod tests { let bond_target = BondRequirementResponse { bond_address: "invalid_address".to_string(), locking_amount_sat: 10000, + escrow_locking_input_amount_without_trade_sum: 10000, }; let trader_input = TraderSettings { electrum_endpoint: "ssl://mempool.space:40002".to_string(), @@ -148,6 +150,7 @@ mod tests { bond_address: "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297" .to_string(), locking_amount_sat: 10000, + escrow_locking_input_amount_without_trade_sum: 10000, }; let trader_input = TraderSettings { electrum_endpoint: "ssl://mempool.space:40002".to_string(), @@ -172,6 +175,7 @@ mod tests { let bond_target = BondRequirementResponse { bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), locking_amount_sat: 10000000000, // Very high amount + escrow_locking_input_amount_without_trade_sum: 10000, }; let trader_input = TraderSettings { electrum_endpoint: "ssl://mempool.space:40002".to_string(), @@ -196,6 +200,7 @@ mod tests { let bond_target = BondRequirementResponse { bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), locking_amount_sat: 0, + escrow_locking_input_amount_without_trade_sum: 10000, }; let trader_input = TraderSettings { electrum_endpoint: "ssl://mempool.space:40002".to_string(), diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 993295f..6b6d3bf 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -112,7 +112,8 @@ impl TradingWallet { } /// returns suitable inputs (binary encoded using bincode, hex serialized, csv formatted) 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)> { + pub fn get_escrow_psbt_inputs(&self, amount_sat: u64) -> Result<(String, String)> { + let mut amount_sat = amount_sat as i64; // convert to signed int for subtraction let mut inputs: Vec = Vec::new(); self.wallet.sync(&self.backend, SyncOptions::default())?; diff --git a/taptrade-cli-demo/trader/src/wallet/musig2_utils.rs b/taptrade-cli-demo/trader/src/wallet/musig2_utils.rs index 8a67c6c..ae9c757 100644 --- a/taptrade-cli-demo/trader/src/wallet/musig2_utils.rs +++ b/taptrade-cli-demo/trader/src/wallet/musig2_utils.rs @@ -7,7 +7,7 @@ /// nonce, as well as flags indicating whether it has been accessed for signing or sharing. /// /// The `generate` function in the `MusigNonce` implementation generates a new `MusigNonce` with a -/// secret nonce based on the current timestamp. +/// secret nonce based on the os rng salted with the current timestamp. /// /// The `get_sec_for_signing` function in the `MusigNonce` implementation returns the secret nonce /// for signing, ensuring that it has not been accessed for signing before. @@ -39,7 +39,7 @@ pub struct MuSigData { pub secret_key: MusigSecretKey, } -// secret nonce has to be used only one time! +/// nonce must not be used more than once #[derive(Debug)] pub struct MusigNonce { secret_nonce: SecNonce,