From 53f3d3ffce777a3f741309d0f14783b4ab2f04f3 Mon Sep 17 00:00:00 2001 From: f321x Date: Thu, 18 Jul 2024 20:42:15 +0200 Subject: [PATCH] add api endpoints, onchain confirmation handling, db handlers --- .../coordinator/src/communication/mod.rs | 49 ++++++++++--- .../coordinator/src/database/mod.rs | 72 ++++++++++++++++++- .../rpc_node/regtest/data/mine-blocks.sh | 5 +- taptrade-cli-demo/trader/src/trading/mod.rs | 2 +- 4 files changed, 113 insertions(+), 15 deletions(-) diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index 3deaf5d..98dae77 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -190,27 +190,55 @@ async fn submit_escrow_psbt( /// Will get polled by the traders once they submitted their PSBT part. The coorinator will return status code 200 once he received both PSBTs and they got mined, /// then the traders will know it is secure to begin with the fiat exchange and can continue with the trade (exchange information in the chat and transfer fiat). -/// We can implement this once the PSBT is done. /// In theory this polling mechanism could also be replaced by the traders scanning the blockchain themself so they could also see once the tx is confirmed. -/// We have to see what makes more sense later, but maybe this would be more elegant. TBD. async fn poll_escrow_confirmation( Extension(database): Extension>, - Extension(wallet): Extension>>, Json(payload): Json, ) -> Result { - panic!("implement") - // let escrow_tx_txid = database.fetch_escrow_txid(&payload.order_id_hex).await?; - // if wallet.is_tx_confirmed(&escrow_tx_txid).await { - // Ok(StatusCode::OK.into_response()) - // } else { - // Ok(StatusCode::ACCEPTED.into_response()) - // } else if not found in database or not published (invalid request) return 404 + if !database + .is_valid_robohash_in_table(&payload.robohash_hex, &payload.order_id_hex) + .await? + { + return Ok(StatusCode::NOT_FOUND.into_response()); + } + if database + .fetch_escrow_tx_confirmation_status(&payload.order_id_hex) + .await? + { + return Ok(StatusCode::OK.into_response()); + } else { + return Ok(StatusCode::ACCEPTED.into_response()); + } } async fn submit_obligation_confirmation( Extension(database): Extension>, Extension(wallet): Extension>>, Json(payload): Json, +) -> Result { + // sanity check if offer is in table and if the escrow tx is confirmed + if !database + .is_valid_robohash_in_table(&payload.robohash_hex, &payload.order_id_hex) + .await? || !database + .fetch_escrow_tx_confirmation_status(&payload.order_id_hex) + .await? + { + return Ok(StatusCode::NOT_FOUND.into_response()); + } + database + .set_trader_happy_true(&payload.order_id_hex, &payload.robohash_hex) + .await?; + Ok(StatusCode::OK.into_response()) +} + +// or + +// gets called if one of the traders wants to initiate escrow (e.g. claiming they didn't receive the fiat) +// before timeout ends +async fn request_escrow( + Extension(database): Extension>, + Extension(wallet): Extension>>, + Json(payload): Json, ) -> Result { panic!("implement") } @@ -248,6 +276,7 @@ pub async fn api_server(coordinator: Arc) -> Result<()> { "/submit-obligation-confirmation", post(submit_obligation_confirmation), ) + .route("/request-escrow", post(request_escrow)) .route("/poll-final-payout", post(poll_final_payout)) .layer(Extension(database)) .layer(Extension(wallet)); diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index 60f0daf..5770353 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -123,7 +123,9 @@ impl CoordinatorDB { escrow_psbt_hex_maker TEXT, escrow_psbt_hex_taker TEXT, escrow_psbt_txid TEXT, - escrow_psbt_is_confirmed INTEGER + escrow_psbt_is_confirmed INTEGER, + maker_happy INTEGER, + taker_happy INTEGER )", // escrow_psbt_is_confirmed will be set 1 once the escrow psbt is confirmed onchain ) .execute(&db_pool) @@ -539,4 +541,72 @@ impl CoordinatorDB { } Ok(()) } + + 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 = ?", + ) + .bind(txid) + .fetch_one(&*self.db_pool) + .await?; + Ok(status.get::("escrow_psbt_is_confirmed") == 1) + } + + pub async fn is_valid_robohash_in_table( + &self, + robohash_hex: &String, + offer_id: &String, + ) -> Result { + let robohash = hex::decode(robohash_hex)?; + let robohash = sqlx::query( + "SELECT 1 FROM taken_offers WHERE (robohash_maker = ? OR robohash_taker = ?) AND offer_id = ?", + ) + .bind(&robohash) + .bind(&robohash) + .bind(offer_id) + .fetch_optional(&*self.db_pool) + .await?; + Ok(robohash.is_some()) + } + + pub async fn fetch_escrow_tx_confirmation_status(&self, offer_id: &String) -> Result { + let status = + sqlx::query("SELECT escrow_psbt_is_confirmed FROM taken_offers WHERE offer_id = ?") + .bind(offer_id) + .fetch_one(&*self.db_pool) + .await?; + Ok(status.get::("escrow_psbt_is_confirmed") == 1) + } + + pub async fn set_trader_happy_true(&self, offer_id: &String, robohash: &String) -> Result<()> { + let robohash_bytes = hex::decode(robohash)?; + + // First, check if the robohash matches the maker or taker + let row = sqlx::query( + "SELECT robohash_maker, robohash_taker FROM taken_offers WHERE offer_id = ?", + ) + .bind(offer_id) + .fetch_one(&*self.db_pool) + .await?; + + let is_maker = row.get::, _>("robohash_maker") == robohash_bytes; + let is_taker = row.get::, _>("robohash_taker") == robohash_bytes; + + if !is_maker && !is_taker { + return Err(anyhow::anyhow!("Robohash does not match maker or taker")); + } + + let query = if is_maker { + "UPDATE taken_offers SET maker_happy = 1 WHERE offer_id = ?" + } else { + "UPDATE taken_offers SET taker_happy = 1 WHERE offer_id = ?" + }; + + sqlx::query(query) + .bind(offer_id) + .execute(&*self.db_pool) + .await?; + + Ok(()) + } } diff --git a/taptrade-cli-demo/rpc_node/regtest/data/mine-blocks.sh b/taptrade-cli-demo/rpc_node/regtest/data/mine-blocks.sh index 34997a4..73d2f3f 100644 --- a/taptrade-cli-demo/rpc_node/regtest/data/mine-blocks.sh +++ b/taptrade-cli-demo/rpc_node/regtest/data/mine-blocks.sh @@ -13,11 +13,10 @@ if echo "$WALLETS" | grep -q "coordinator_wallet"; then else echo "Wallet does not exist. Creating wallet..." bitcoin-cli -regtest -datadir="/home/bitcoin/.bitcoin" createwallet "coordinator_wallet" + # Generate initial blocks + bitcoin-cli -regtest -datadir="/home/bitcoin/.bitcoin" -rpcwallet="coordinator_wallet" -generate 101 fi -# Generate initial blocks -bitcoin-cli -regtest -datadir="/home/bitcoin/.bitcoin" -rpcwallet="coordinator_wallet" -generate 101 - # Generate a block every 120 seconds while true; do bitcoin-cli -regtest -datadir="/home/bitcoin/.bitcoin" -rpcwallet="coordinator_wallet" -generate 1 diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 401c19a..dd7319b 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -50,7 +50,7 @@ pub fn run_maker(maker_config: &TraderSettings) -> Result<()> { info!("Waiting for other party to confirm the trade."); let payout_keyspend_psbt = IsOfferReadyRequest::poll_payout(maker_config, &offer)?; } else { - error!("Trade failed."); + error!("Trade failed. Initiating escrow mode."); panic!("Escrow to be implemented!"); } Ok(())