From 228809853f59ab9bb97fe7389ed22121bae9ae79 Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:47:07 +0000 Subject: [PATCH 01/10] fetch taker psbt --- .../trader/src/communication/api.rs | 18 +++-- .../src/communication/taker_requests.rs | 70 ++++++++++--------- .../trader/src/trading/taker_utils.rs | 35 +++++++--- taptrade-cli-demo/trader/src/wallet/bond.rs | 4 -- taptrade-cli-demo/trader/src/wallet/mod.rs | 5 +- 5 files changed, 76 insertions(+), 56 deletions(-) diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 9f9caea..4739519 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -50,6 +50,7 @@ pub struct OfferTakenResponse { // Taker structures // +// request all fitting offers from the coordinator #[derive(Debug, Serialize)] pub struct OffersRequest { pub buy_offers: bool, // true if looking for buy offers, false if looking for sell offers @@ -57,19 +58,22 @@ pub struct OffersRequest { pub amount_max_sat: u64, } -#[derive(Debug, Deserialize, Serialize)] -pub struct PublicOffer { - pub amount_sat: u64, - pub offer_id_hex: String, -} - +// response of the coordinator, containing all fitting offers to the OffersRequest request #[derive(Debug, Deserialize)] pub struct PublicOffers { pub offers: Option>, // don't include offers var in return json if no offers are available } +// Offer information of each offer returned by the previous response +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct PublicOffer { + pub amount_sat: u64, + pub offer_id_hex: String, +} + +// request to receive the escrow psbt to sign for the specified offer to take it #[derive(Debug, Serialize)] -pub struct RequestOfferPsbt { +pub struct OfferPsbtRequest { pub offer: PublicOffer, pub trade_data: BondSubmissionRequest, } diff --git a/taptrade-cli-demo/trader/src/communication/taker_requests.rs b/taptrade-cli-demo/trader/src/communication/taker_requests.rs index a6771da..0e903ba 100644 --- a/taptrade-cli-demo/trader/src/communication/taker_requests.rs +++ b/taptrade-cli-demo/trader/src/communication/taker_requests.rs @@ -42,40 +42,44 @@ impl PublicOffers { } } -impl PublicOffer { tbd - // pub fn take(&self, taker_config: &TraderSettings) -> Result { - // let client = reqwest::blocking::Client::new(); - // let res = client - // .post(format!( - // "{}{}", - // taker_config.coordinator_endpoint, "/take-offer" - // )) - // .json(self) - // .send()? - // .json::()?; - // Ok(res) - // } +impl PublicOffer { + pub fn request_bond(&self, taker_config: &TraderSettings) -> Result { + let client = reqwest::blocking::Client::new(); + let res = client + .post(format!( + "{}{}", + taker_config.coordinator_endpoint, "/request-taker-bond" + )) + .json(self) + .send()? + .json::()?; + Ok(res) + } } -impl OfferTakenRequest { // tbd - // pub fn taker_request( - // bond: &Bond, - // mut musig_data: &MuSigData, - // taker_config: &TraderSettings, - // ) -> Result { - // let request = RequestOfferPsbt { - // offer: - // }; +impl OfferPsbtRequest { + pub fn taker_request( + offer: &PublicOffer, + trade_data: BondSubmissionRequest, + taker_config: &TraderSettings, + ) -> Result { + let request = OfferPsbtRequest { + offer: offer.clone(), + trade_data, + }; - // let client = reqwest::blocking::Client::new(); - // let res = client - // .post(format!( - // "{}{}", - // taker_config.coordinator_endpoint, "/submit-taker-bond" - // )) - // .json(self) - // .send()? - // .json::()?; - // Ok(res) - // } + let client = reqwest::blocking::Client::new(); + let res = client + .post(format!( + "{}{}", + taker_config.coordinator_endpoint, "/submit-taker-bond" + )) + .json(&request) + .send()? + .json::()?; + + let psbt_bytes = hex::decode(res.trade_psbt_hex_to_sign)?; + let psbt = PartiallySignedTransaction::deserialize(&psbt_bytes)?; + Ok(psbt) + } } diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 876ece3..592cecc 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -1,16 +1,33 @@ +use bdk::electrum_client::Request; + +use crate::communication::api::{OfferPsbtRequest, RequestOfferPsbt}; + use super::utils::*; use super::*; impl ActiveOffer { - // pub fn take( tbd - // trading_wallet: &TradingWallet, - // taker_config: &TraderSettings, - // offer: &PublicOffer, - // ) -> Result { - // let bond_conditions: BondRequirementResponse = offer.take(taker_config)?; - // let (bond, mut musig_data, payout_address) = - // trading_wallet.trade_onchain_assembly(&bond_conditions, taker_config)?; - // let trading_psbt = + pub fn take( + trading_wallet: &TradingWallet, + taker_config: &TraderSettings, + offer: &PublicOffer, + ) -> Result { + // fetching the bond requirements for the requested Offer (amount, locking address) + let bond_conditions: BondRequirementResponse = offer.request_bond(taker_config)?; + + // 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_conditions, taker_config)?; + + // now we submit the signed bond transaction to the coordinator and receive the escrow PSBT we have to sign + // in exchange + let bond_submission_request = BondSubmissionRequest::prepare_bond_request( + &bond, + &payout_address, + &mut musig_data, + taker_config, + )?; + let escrow_contract_psbt = + OfferPsbtRequest::taker_request(offer, bond_submission_request, taker_config)?; } pub fn wait_on_maker(&self) -> Result<()> { diff --git a/taptrade-cli-demo/trader/src/wallet/bond.rs b/taptrade-cli-demo/trader/src/wallet/bond.rs index 4952199..bee743c 100644 --- a/taptrade-cli-demo/trader/src/wallet/bond.rs +++ b/taptrade-cli-demo/trader/src/wallet/bond.rs @@ -57,7 +57,3 @@ impl Bond { Ok(psbt) } } - -// impl BranchAndBoundCoinSelection -// pub fn new(size_of_change: u64) -> Self -// Create new instance with target size for change output diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 68c1e7b..3d67c6f 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -63,12 +63,11 @@ impl TradingWallet { offer_conditions: &BondRequirementResponse, trader_config: &TraderSettings, ) -> Result<(PartiallySignedTransaction, MuSigData, AddressInfo)> { - let trading_wallet = self.wallet; + let trading_wallet = &self.wallet; let bond = Bond::assemble(&self.wallet, &offer_conditions, trader_config)?; let payout_address: AddressInfo = trading_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?; - let mut musig_data = - MuSigData::create(&trader_config.wallet_xprv, trading_wallet.secp_ctx())?; + let musig_data = MuSigData::create(&trader_config.wallet_xprv, trading_wallet.secp_ctx())?; Ok((bond, musig_data, payout_address)) } From 2758511211b70fda7b35a26c392b2669aae9810f Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:53:10 +0000 Subject: [PATCH 02/10] comments --- taptrade-cli-demo/trader/src/trading/maker_utils.rs | 2 +- taptrade-cli-demo/trader/src/trading/mod.rs | 2 +- taptrade-cli-demo/trader/src/trading/taker_utils.rs | 12 ++++++++++-- taptrade-cli-demo/trader/src/wallet/mod.rs | 7 +++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index 0b8d960..cd1b57b 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -16,7 +16,7 @@ impl ActiveOffer { let (bond, mut musig_data, payout_address) = trading_wallet.trade_onchain_assembly(&offer_conditions, maker_config)?; - let submission_result = BondSubmissionRequest::send( + let submission_result = BondSubmissionRequest::send_maker( &maker_config.robosats_robohash_hex, &bond, &mut musig_data, diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index af073dd..e2af521 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -45,7 +45,7 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { let selected_offer: &PublicOffer = available_offers.ask_user_to_select()?; let accepted_offer = ActiveOffer::take(&wallet, taker_config, selected_offer)?; - accepted_offer.wait_on_maker(); + // accepted_offer.wait_on_maker(); Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 592cecc..e41d9ac 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -1,6 +1,6 @@ use bdk::electrum_client::Request; -use crate::communication::api::{OfferPsbtRequest, RequestOfferPsbt}; +use crate::communication::api::OfferPsbtRequest; use super::utils::*; use super::*; @@ -10,7 +10,9 @@ impl ActiveOffer { trading_wallet: &TradingWallet, taker_config: &TraderSettings, offer: &PublicOffer, - ) -> Result { + ) -> Result<()> { + // return Ok(Active offer) + // fetching the bond requirements for the requested Offer (amount, locking address) let bond_conditions: BondRequirementResponse = offer.request_bond(taker_config)?; @@ -28,6 +30,12 @@ impl ActiveOffer { )?; let escrow_contract_psbt = OfferPsbtRequest::taker_request(offer, bond_submission_request, taker_config)?; + + // now we have to verify, sign and submit the escrow psbt again + if !trading_wallet.validate_taker_psbt(&escrow_contract_psbt) { + panic!("taker psbt invalid!"); + } + Ok(()) } pub fn wait_on_maker(&self) -> Result<()> { diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 3d67c6f..4653a0d 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -71,4 +71,11 @@ impl TradingWallet { Ok((bond, musig_data, payout_address)) } + + // 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) -> bool { + false + } } From e4cf054fd97d3ddc1f13305ffe3ff6bb5381742d Mon Sep 17 00:00:00 2001 From: fbock Date: Mon, 17 Jun 2024 16:01:34 +0200 Subject: [PATCH 03/10] add routes to mockoon json --- taptrade_api_mockoon.json | 43 ++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/taptrade_api_mockoon.json b/taptrade_api_mockoon.json index b0429b4..00e1fd1 100644 --- a/taptrade_api_mockoon.json +++ b/taptrade_api_mockoon.json @@ -78,7 +78,7 @@ "body": "{\n \"trade_psbt_hex_to_sign\": \"DEADBEEF\",\n}", "latency": 0, "statusCode": 200, - "label": "Returned of the offer has been taken", + "label": "Returned if the offer has been taken", "headers": [], "bodyType": "INLINE", "filePath": "", @@ -94,7 +94,7 @@ }, { "uuid": "9ada9097-97e3-40c3-b0cf-d0eec3587027", - "body": "{}", + "body": "", "latency": 0, "statusCode": 204, "label": "Returned if the requested offer is not yet taken", @@ -126,7 +126,7 @@ "body": " \"offers\": [\n {\n \"amount_sat\": 1000,\n \"offer_id_hex\": \"abc123\"\n },\n {\n \"amount_sat\": 2000,\n \"offer_id_hex\": \"def456\"\n }\n ]", "latency": 0, "statusCode": 200, - "label": "", + "label": "Returns a list of available offers, requested with OffersRequest", "headers": [], "bodyType": "INLINE", "filePath": "", @@ -148,14 +148,43 @@ "type": "http", "documentation": "", "method": "post", - "endpoint": "take-offer", + "endpoint": "request-taker-bond", "responses": [ { "uuid": "e527cfa3-eaf6-4972-882b-b723084dfe49", "body": "{\n \"bond_address\": \"tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm\",\n \"locking_amount\": 123456\n}", "latency": 0, "statusCode": 200, - "label": "", + "label": "Gets requested with PublicOffer Json", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "cb932396-c73e-4a4c-9f1a-b4de5250cb16", + "type": "http", + "documentation": "gets requested with OfferPsbtRequest", + "method": "post", + "endpoint": "submit-taker-bond", + "responses": [ + { + "uuid": "b852f55c-5c97-47d2-a149-cbf6a0fad3e1", + "body": "{\n \"trade_psbt_hex_to_sign\": \"INVALID_EXAMPLE_37346634343237352D373930622D343631342D626139332D65636637323565303638376337346634343237352D373930622D343631342D626139332D65636637323565303638376337346634343237352D373930622D343631342D626139332D65636637323565303638376337346634343237352D373930622D343631342D626139332D65636637323565303638376337346634343237352D373930622D343631342D626139332D656366373235653036383763\",\n}", + "latency": 0, + "statusCode": 200, + "label": "returns OfferTakenResponse if bond is valid", "headers": [], "bodyType": "INLINE", "filePath": "", @@ -193,6 +222,10 @@ { "type": "route", "uuid": "ee4bb798-c787-43ae-9a34-64568ba8c31b" + }, + { + "type": "route", + "uuid": "cb932396-c73e-4a4c-9f1a-b4de5250cb16" } ], "proxyMode": false, From 3c24424a9f68af79ef5fde53d01a8881a21ab009 Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 18 Jun 2024 08:04:37 +0000 Subject: [PATCH 04/10] add taker escrow psbt submission --- .../trader/src/communication/api.rs | 8 +++++ .../trader/src/communication/mod.rs | 2 +- .../src/communication/taker_requests.rs | 29 ++++++++++++++++ taptrade-cli-demo/trader/src/trading/mod.rs | 5 ++- .../trader/src/trading/taker_utils.rs | 34 +++++++++++++------ taptrade-cli-demo/trader/src/trading/utils.rs | 2 +- taptrade-cli-demo/trader/src/wallet/mod.rs | 18 +++++++--- 7 files changed, 78 insertions(+), 20 deletions(-) diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 4739519..63dd3e2 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -77,3 +77,11 @@ pub struct OfferPsbtRequest { pub offer: PublicOffer, pub trade_data: BondSubmissionRequest, } + +// submit signed escrow psbt back to coordinator in a Json like this +#[derive(Debug, Serialize)] +pub struct PsbtSubmissionRequest { + pub signed_psbt_hex: String, + pub offer_id_hex: String, + pub robohash_hex: String, +} diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index 15788a8..63a77ed 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -9,7 +9,7 @@ use crate::{ use anyhow::{anyhow, Result}; use api::{ BondRequirementResponse, BondSubmissionRequest, OfferTakenRequest, OfferTakenResponse, - OrderActivatedResponse, OrderRequest, + OrderActivatedResponse, OrderRequest, PsbtSubmissionRequest, }; use bdk::bitcoin::consensus::encode::serialize_hex; use bdk::{ diff --git a/taptrade-cli-demo/trader/src/communication/taker_requests.rs b/taptrade-cli-demo/trader/src/communication/taker_requests.rs index 0e903ba..135d09a 100644 --- a/taptrade-cli-demo/trader/src/communication/taker_requests.rs +++ b/taptrade-cli-demo/trader/src/communication/taker_requests.rs @@ -83,3 +83,32 @@ impl OfferPsbtRequest { Ok(psbt) } } + +impl PsbtSubmissionRequest { + pub fn submit_taker_psbt( + psbt: &PartiallySignedTransaction, + offer_id_hex: String, + taker_config: &TraderSettings, + ) -> Result<()> { + let request = PsbtSubmissionRequest { + signed_psbt_hex: psbt.serialize_hex(), + offer_id_hex, + robohash_hex: taker_config.robosats_robohash_hex.clone(), + }; + let client = reqwest::blocking::Client::new(); + let res = client + .post(format!( + "{}{}", + taker_config.coordinator_endpoint, "/submit-taker-psbt" + )) + .json(&request) + .send()?; + if res.status() != 200 { + return Err(anyhow!( + "Submitting taker psbt failed. Status: {}", + res.status() + )); + } + Ok(()) + } +} diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index e2af521..b4ab1d1 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -43,9 +43,8 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { available_offers = PublicOffers::fetch(taker_config)?; } let selected_offer: &PublicOffer = available_offers.ask_user_to_select()?; - let accepted_offer = ActiveOffer::take(&wallet, taker_config, selected_offer)?; - - // accepted_offer.wait_on_maker(); + let accepted_offer = + ActiveOffer::take(&wallet, taker_config, selected_offer)?.wait_on_maker()?; Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index e41d9ac..17d763a 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -1,6 +1,6 @@ use bdk::electrum_client::Request; -use crate::communication::api::OfferPsbtRequest; +use crate::communication::api::{OfferPsbtRequest, PsbtSubmissionRequest}; use super::utils::*; use super::*; @@ -10,9 +10,7 @@ impl ActiveOffer { trading_wallet: &TradingWallet, taker_config: &TraderSettings, offer: &PublicOffer, - ) -> Result<()> { - // return Ok(Active offer) - + ) -> Result { // fetching the bond requirements for the requested Offer (amount, locking address) let bond_conditions: BondRequirementResponse = offer.request_bond(taker_config)?; @@ -28,18 +26,32 @@ impl ActiveOffer { &mut musig_data, taker_config, )?; - let escrow_contract_psbt = + let mut escrow_contract_psbt = OfferPsbtRequest::taker_request(offer, bond_submission_request, taker_config)?; // now we have to verify, sign and submit the escrow psbt again - if !trading_wallet.validate_taker_psbt(&escrow_contract_psbt) { - panic!("taker psbt invalid!"); - } - Ok(()) + trading_wallet + .validate_taker_psbt(&escrow_contract_psbt)? + .sign_escrow_psbt(&mut escrow_contract_psbt)?; + + // submit signed escrow psbt back to coordinator + PsbtSubmissionRequest::submit_taker_psbt( + &escrow_contract_psbt, + offer.offer_id_hex.clone(), + taker_config, + )?; + + Ok(ActiveOffer { + order_id_hex: offer.offer_id_hex.clone(), + used_musig_config: musig_data, + used_bond: bond, + expected_payout_address: payout_address, + escrow_psbt: escrow_contract_psbt, + }) } - pub fn wait_on_maker(&self) -> Result<()> { + pub fn wait_on_maker(self) -> Result { // tbd - Ok(()) + Ok(self) } } diff --git a/taptrade-cli-demo/trader/src/trading/utils.rs b/taptrade-cli-demo/trader/src/trading/utils.rs index 0c73f6b..4ed1365 100644 --- a/taptrade-cli-demo/trader/src/trading/utils.rs +++ b/taptrade-cli-demo/trader/src/trading/utils.rs @@ -4,8 +4,8 @@ use super::*; #[derive(Debug)] pub struct ActiveOffer { pub order_id_hex: String, - pub bond_locked_until_timestamp: u128, pub used_musig_config: MuSigData, pub used_bond: PartiallySignedTransaction, pub expected_payout_address: AddressInfo, + pub escrow_psbt: PartiallySignedTransaction, } diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index 4653a0d..d0cd1f1 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -3,7 +3,7 @@ pub mod musig2; pub mod wallet_utils; use crate::{cli::TraderSettings, communication::api::BondRequirementResponse}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use bdk::{ bitcoin::{self, bip32::ExtendedPrivKey, psbt::PartiallySignedTransaction, Network}, blockchain::ElectrumBlockchain, @@ -13,7 +13,7 @@ use bdk::{ miniscript::Descriptor, template::{Bip86, DescriptorTemplate}, wallet::AddressInfo, - KeychainKind, SyncOptions, Wallet, + KeychainKind, SignOptions, SyncOptions, Wallet, }; use bond::Bond; use musig2::MuSigData; @@ -75,7 +75,17 @@ 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) -> bool { - false + pub fn validate_taker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> { + dbg!("IMPLEMENT TAKER PSBT VALIDATION!"); + // tbd once the trade psbt is implemented + Ok(self) + } + + pub fn sign_escrow_psbt(&self, escrow_psbt: &mut PartiallySignedTransaction) -> Result<&Self> { + let finalized = self.wallet.sign(escrow_psbt, SignOptions::default())?; + if !finalized { + return Err(anyhow!("Signing of taker escrow psbt failed!")); + } + Ok(self) } } From 613a83b8e47948ffe3f09ef985d7abe25681f290 Mon Sep 17 00:00:00 2001 From: fbock Date: Tue, 18 Jun 2024 10:10:07 +0200 Subject: [PATCH 05/10] add mockoon route for taker psbt submission --- taptrade_api_mockoon.json | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/taptrade_api_mockoon.json b/taptrade_api_mockoon.json index 00e1fd1..74fe4e2 100644 --- a/taptrade_api_mockoon.json +++ b/taptrade_api_mockoon.json @@ -200,6 +200,54 @@ } ], "responseMode": null + }, + { + "uuid": "34bda159-89cf-453c-bf5c-fa532b35f202", + "type": "http", + "documentation": "Taker submits the psbt as json to the coordinator. ", + "method": "post", + "endpoint": "submit-taker-psbt", + "responses": [ + { + "uuid": "e9f64b19-94d0-4356-8892-d05466abbc5d", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "Returned if psbt/signature is valid.", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "fdfb8fc1-1d54-46fc-b5ad-174f761120c1", + "body": "{}", + "latency": 0, + "statusCode": 406, + "label": "Returned if the signature is invalid or the psbt has been changed ", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null } ], "rootChildren": [ @@ -226,6 +274,10 @@ { "type": "route", "uuid": "cb932396-c73e-4a4c-9f1a-b4de5250cb16" + }, + { + "type": "route", + "uuid": "34bda159-89cf-453c-bf5c-fa532b35f202" } ], "proxyMode": false, From 98d4475a2ce3d81e8819534d3dfa969176d138fa Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:23:03 +0000 Subject: [PATCH 06/10] add taker wait on fiat confirmation --- .../trader/src/communication/api.rs | 8 +++++ .../trader/src/communication/mod.rs | 3 +- .../src/communication/taker_requests.rs | 29 +++++++++++++++++++ .../trader/src/trading/maker_utils.rs | 4 +-- taptrade-cli-demo/trader/src/trading/mod.rs | 6 +++- .../trader/src/trading/taker_utils.rs | 23 +++++++++++---- taptrade-cli-demo/trader/src/trading/utils.rs | 4 +-- 7 files changed, 66 insertions(+), 11 deletions(-) diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 63dd3e2..cf63b22 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -85,3 +85,11 @@ pub struct PsbtSubmissionRequest { pub offer_id_hex: String, pub robohash_hex: String, } + +// request polled to check if the maker has submitted his escrow transaction +// and the escrow transaction is confirmed once this returns 200 the chat can open +#[derive(Debug, Serialize)] +pub struct IsOfferReadyRequest { + pub robohash_hex: String, + pub offer_id_hex: String, +} diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index 63a77ed..f721bc8 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -17,6 +17,7 @@ use bdk::{ wallet::AddressInfo, }; use serde::{Deserialize, Serialize}; +use std::{thread::sleep, time::Duration}; impl BondRequirementResponse { fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { @@ -107,7 +108,7 @@ impl OfferTakenResponse { let request = OfferTakenRequest { // maybe can be made a bit more efficient (less clone) robohash_hex: trader_setup.robosats_robohash_hex.clone(), - order_id_hex: offer.order_id_hex.clone(), + order_id_hex: offer.offer_id_hex.clone(), }; let client = reqwest::blocking::Client::new(); let res = client diff --git a/taptrade-cli-demo/trader/src/communication/taker_requests.rs b/taptrade-cli-demo/trader/src/communication/taker_requests.rs index 135d09a..5162ff5 100644 --- a/taptrade-cli-demo/trader/src/communication/taker_requests.rs +++ b/taptrade-cli-demo/trader/src/communication/taker_requests.rs @@ -112,3 +112,32 @@ impl PsbtSubmissionRequest { Ok(()) } } + +impl IsOfferReadyRequest { + pub fn poll(taker_config: &TraderSettings, offer: &ActiveOffer) -> Result<()> { + let request = IsOfferReadyRequest { + robohash_hex: taker_config.robosats_robohash_hex.clone(), + offer_id_hex: offer.offer_id_hex.clone(), + }; + let client = reqwest::blocking::Client::new(); + loop { + let res = client + .post(format!( + "{}{}", + taker_config.coordinator_endpoint, "/poll-offer-status-taker" + )) + .json(&request) + .send()?; + if res.status() == 200 { + return Ok(()); + } else if res.status() != 201 { + return Err(anyhow!( + "Submitting taker psbt failed. Status: {}", + res.status() + )); + } + // Sleep for 10 sec and poll again + sleep(Duration::from_secs(10)); + } + } +} diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index cd1b57b..bbbd71b 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -24,11 +24,11 @@ impl ActiveOffer { maker_config, )?; Ok(ActiveOffer { - order_id_hex: submission_result.order_id_hex, - bond_locked_until_timestamp: submission_result.bond_locked_until_timestamp, + offer_id_hex: submission_result.order_id_hex, used_musig_config: musig_data, used_bond: bond, expected_payout_address: payout_address, + escrow_psbt: None, }) } diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index b4ab1d1..8567318 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -43,8 +43,12 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { available_offers = PublicOffers::fetch(taker_config)?; } let selected_offer: &PublicOffer = available_offers.ask_user_to_select()?; + + // take selected offer and wait for maker to sign his input to the ecrow transaction let accepted_offer = - ActiveOffer::take(&wallet, taker_config, selected_offer)?.wait_on_maker()?; + ActiveOffer::take(&wallet, taker_config, selected_offer)?.wait_on_maker(taker_config)?; + + accepted_offer.wait_on_fiat_confirmation()?; Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 17d763a..516f9ee 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -1,6 +1,6 @@ use bdk::electrum_client::Request; -use crate::communication::api::{OfferPsbtRequest, PsbtSubmissionRequest}; +use crate::communication::api::{IsOfferReadyRequest, OfferPsbtRequest, PsbtSubmissionRequest}; use super::utils::*; use super::*; @@ -42,16 +42,29 @@ impl ActiveOffer { )?; Ok(ActiveOffer { - order_id_hex: offer.offer_id_hex.clone(), + offer_id_hex: offer.offer_id_hex.clone(), used_musig_config: musig_data, used_bond: bond, expected_payout_address: payout_address, - escrow_psbt: escrow_contract_psbt, + escrow_psbt: Some(escrow_contract_psbt), }) } - pub fn wait_on_maker(self) -> Result { - // tbd + pub fn wait_on_maker(self, taker_config: &TraderSettings) -> Result { + IsOfferReadyRequest::poll(taker_config, &self)?; + Ok(self) + } + + pub fn wait_on_fiat_confirmation(&self) -> Result<&Self> { + // let user confirm in CLI that the fiat payment has been sent/receivec + loop { + println!("Please confirm that the fiat payment has been sent/received. (y/N)"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + if input.trim().to_lowercase() == "y" { + break; + } + } Ok(self) } } diff --git a/taptrade-cli-demo/trader/src/trading/utils.rs b/taptrade-cli-demo/trader/src/trading/utils.rs index 4ed1365..3d4ca35 100644 --- a/taptrade-cli-demo/trader/src/trading/utils.rs +++ b/taptrade-cli-demo/trader/src/trading/utils.rs @@ -3,9 +3,9 @@ use super::*; #[derive(Debug)] pub struct ActiveOffer { - pub order_id_hex: String, + pub offer_id_hex: String, pub used_musig_config: MuSigData, pub used_bond: PartiallySignedTransaction, pub expected_payout_address: AddressInfo, - pub escrow_psbt: PartiallySignedTransaction, + pub escrow_psbt: Option, } From 11fb176977b6357c61832f1eb87786ca329ede89 Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:53:00 +0000 Subject: [PATCH 07/10] maker fiat confirmation --- taptrade-cli-demo/trader/src/trading/mod.rs | 6 +++++- taptrade-cli-demo/trader/src/wallet/mod.rs | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 8567318..2b1fd14 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -28,7 +28,11 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { let offer = ActiveOffer::create(&wallet, maker_config)?; dbg!(&offer); - let trade_psbt = offer.wait_until_taken(maker_config)?; + let mut escrow_contract_psbt = offer.wait_until_taken(maker_config)?; + + wallet + .validate_maker_psbt(&escrow_contract_psbt)? + .sign_escrow_psbt(&mut escrow_contract_psbt)?; Ok(()) } diff --git a/taptrade-cli-demo/trader/src/wallet/mod.rs b/taptrade-cli-demo/trader/src/wallet/mod.rs index d0cd1f1..dfd1186 100644 --- a/taptrade-cli-demo/trader/src/wallet/mod.rs +++ b/taptrade-cli-demo/trader/src/wallet/mod.rs @@ -77,7 +77,7 @@ impl TradingWallet { // input amount should be the bond amount when buying, pub fn validate_taker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> { dbg!("IMPLEMENT TAKER PSBT VALIDATION!"); - // tbd once the trade psbt is implemented + // tbd once the trade psbt is implemented on coordinator side Ok(self) } @@ -88,4 +88,10 @@ impl TradingWallet { } Ok(self) } + + pub fn validate_maker_psbt(&self, psbt: &PartiallySignedTransaction) -> Result<&Self> { + dbg!("IMPLEMENT MAKER PSBT VALIDATION!"); + // tbd once the trade psbt is implemented on coordinator side + Ok(self) + } } From bd8deb2b4cb6238066619c0b0cc86f669201bf2e Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:24:23 +0000 Subject: [PATCH 08/10] add psbt submission for maker --- .../trader/src/communication/mod.rs | 29 +++++++++++++++++++ .../src/communication/taker_requests.rs | 29 ------------------- taptrade-cli-demo/trader/src/trading/mod.rs | 8 +++++ .../trader/src/trading/taker_utils.rs | 2 +- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index f721bc8..c20659c 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -127,3 +127,32 @@ impl OfferTakenResponse { } } } + +impl PsbtSubmissionRequest { + pub fn submit_escrow_psbt( + psbt: &PartiallySignedTransaction, + offer_id_hex: String, + taker_config: &TraderSettings, + ) -> Result<()> { + let request = PsbtSubmissionRequest { + signed_psbt_hex: psbt.serialize_hex(), + offer_id_hex, + robohash_hex: taker_config.robosats_robohash_hex.clone(), + }; + let client = reqwest::blocking::Client::new(); + let res = client + .post(format!( + "{}{}", + taker_config.coordinator_endpoint, "/submit-escrow-psbt" + )) + .json(&request) + .send()?; + if res.status() != 200 { + return Err(anyhow!( + "Submitting escrow psbt failed. Status: {}", + res.status() + )); + } + Ok(()) + } +} diff --git a/taptrade-cli-demo/trader/src/communication/taker_requests.rs b/taptrade-cli-demo/trader/src/communication/taker_requests.rs index 5162ff5..d964cf2 100644 --- a/taptrade-cli-demo/trader/src/communication/taker_requests.rs +++ b/taptrade-cli-demo/trader/src/communication/taker_requests.rs @@ -84,35 +84,6 @@ impl OfferPsbtRequest { } } -impl PsbtSubmissionRequest { - pub fn submit_taker_psbt( - psbt: &PartiallySignedTransaction, - offer_id_hex: String, - taker_config: &TraderSettings, - ) -> Result<()> { - let request = PsbtSubmissionRequest { - signed_psbt_hex: psbt.serialize_hex(), - offer_id_hex, - robohash_hex: taker_config.robosats_robohash_hex.clone(), - }; - let client = reqwest::blocking::Client::new(); - let res = client - .post(format!( - "{}{}", - taker_config.coordinator_endpoint, "/submit-taker-psbt" - )) - .json(&request) - .send()?; - if res.status() != 200 { - return Err(anyhow!( - "Submitting taker psbt failed. Status: {}", - res.status() - )); - } - Ok(()) - } -} - impl IsOfferReadyRequest { pub fn poll(taker_config: &TraderSettings, offer: &ActiveOffer) -> Result<()> { let request = IsOfferReadyRequest { diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 2b1fd14..81847ba 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -34,6 +34,14 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { .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, + offer.offer_id_hex.clone(), + taker_config, + )?; + // wait for confirmation + Ok(()) } diff --git a/taptrade-cli-demo/trader/src/trading/taker_utils.rs b/taptrade-cli-demo/trader/src/trading/taker_utils.rs index 516f9ee..ddd9533 100644 --- a/taptrade-cli-demo/trader/src/trading/taker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/taker_utils.rs @@ -35,7 +35,7 @@ impl ActiveOffer { .sign_escrow_psbt(&mut escrow_contract_psbt)?; // submit signed escrow psbt back to coordinator - PsbtSubmissionRequest::submit_taker_psbt( + PsbtSubmissionRequest::submit_escrow_psbt( &escrow_contract_psbt, offer.offer_id_hex.clone(), taker_config, From 42424672ed8d21ab3c046a716125c4bdedcb8896 Mon Sep 17 00:00:00 2001 From: Felix <51097237+f321x@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:26:26 +0000 Subject: [PATCH 09/10] fix maker psbt submission --- taptrade-cli-demo/trader/src/trading/maker_utils.rs | 4 ++-- taptrade-cli-demo/trader/src/trading/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index bbbd71b..31a755d 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -35,12 +35,12 @@ impl ActiveOffer { // polling until offer is taken, in production a more efficient way would make sense // returns the PSBT of the escrow trade transaction we have to validate, sign and return pub fn wait_until_taken( - self, + &self, trader_config: &TraderSettings, ) -> Result { loop { thread::sleep(Duration::from_secs(10)); - if let Some(offer_taken_response) = OfferTakenResponse::check(&self, trader_config)? { + if let Some(offer_taken_response) = OfferTakenResponse::check(self, trader_config)? { let psbt_bytes = hex::decode(offer_taken_response.trade_psbt_hex_to_sign)?; let psbt = PartiallySignedTransaction::deserialize(&psbt_bytes)?; return Ok(psbt); diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 81847ba..f96e6a9 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -7,7 +7,7 @@ use crate::{ cli::TraderSettings, communication::api::{ BondRequirementResponse, BondSubmissionRequest, OfferTakenRequest, OfferTakenResponse, - PublicOffer, PublicOffers, + PsbtSubmissionRequest, PublicOffer, PublicOffers, }, wallet::{ bond::Bond, @@ -38,7 +38,7 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { PsbtSubmissionRequest::submit_escrow_psbt( &escrow_contract_psbt, offer.offer_id_hex.clone(), - taker_config, + maker_config, )?; // wait for confirmation From 4126361163a6546d9be551a9177bb84f55088e6f Mon Sep 17 00:00:00 2001 From: fbock Date: Tue, 18 Jun 2024 18:27:29 +0200 Subject: [PATCH 10/10] change mockoon endpoint --- docs/TapTrade_obs/.obsidian/workspace.json | 16 ++++++++-------- taptrade_api_mockoon.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/TapTrade_obs/.obsidian/workspace.json b/docs/TapTrade_obs/.obsidian/workspace.json index da58f80..8ff7582 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 pipeline 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", "viewState": { - "x": 137.064506277392, - "y": 733.2796866482448, - "zoom": 0.27410685221354214 + "x": 31, + "y": -256.4238119356301, + "zoom": -0.4572735104698888 } } } @@ -88,7 +88,7 @@ "state": { "type": "backlink", "state": { - "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -105,7 +105,7 @@ "state": { "type": "outgoing-link", "state": { - "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", + "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", "linksCollapsed": false, "unlinkedCollapsed": true } @@ -128,7 +128,7 @@ "state": { "type": "outline", "state": { - "file": "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas" + "file": "Research/Trade Pipelines/new concepts/concept locking script 1.canvas" } } } @@ -151,11 +151,11 @@ }, "active": "98e8706e39a96828", "lastOpenFiles": [ + "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", "Research/Trade Pipelines/new concepts/concept locking script 1.canvas", "Research/Bitcoin fundamentals/Signature and Flags.canvas", "Research/Bitcoin fundamentals/Knowledge sources.md", "Research/Implementation/CLI demonstrator architecture/demonstrator architecture.canvas", - "Research/Trade Pipelines/new concepts/concept pipeline 1.canvas", "Research/Implementation/Libraries.md", "Research/Implementation/BDK.md", "Research/Implementation/UI ideas.canvas", diff --git a/taptrade_api_mockoon.json b/taptrade_api_mockoon.json index 74fe4e2..42637e3 100644 --- a/taptrade_api_mockoon.json +++ b/taptrade_api_mockoon.json @@ -206,7 +206,7 @@ "type": "http", "documentation": "Taker submits the psbt as json to the coordinator. ", "method": "post", - "endpoint": "submit-taker-psbt", + "endpoint": "submit-escrow-psbt", "responses": [ { "uuid": "e9f64b19-94d0-4356-8892-d05466abbc5d",