diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index d3a3eb7..3deaf5d 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -138,12 +138,14 @@ async fn submit_taker_bond( panic!("Trade contract PSBT not implemented!"); let trade_contract_psbt_taker = "".to_string(); // implement psbt let trade_contract_psbt_maker = "".to_string(); // implement psbt + let escrow_tx_txid: String = "".to_string(); // implement txid of psbt database .add_taker_info_and_move_table( &payload, &trade_contract_psbt_maker, &trade_contract_psbt_taker, + escrow_tx_txid, ) .await?; Ok(Json(OfferTakenResponse { diff --git a/taptrade-cli-demo/coordinator/src/coordinator/_verify_bond.old b/taptrade-cli-demo/coordinator/src/coordinator/_verify_bond.old deleted file mode 100644 index 225ac9b..0000000 --- a/taptrade-cli-demo/coordinator/src/coordinator/_verify_bond.old +++ /dev/null @@ -1,84 +0,0 @@ -use bdk::bitcoin::consensus::encode::deserialize; -use bdk::bitcoin::psbt::PartiallySignedTransaction; -use bdk::bitcoin::Transaction; -use bdk::database::MemoryDatabase; -use bdk::SignOptions; -use bdk::Wallet; - -use crate::communication::api::BondSubmissionRequest; -use crate::communication::api::OrderActivatedResponse; - -use anyhow::{anyhow, Result}; -use hex; - -pub fn verify_and_respond( - bond_submission: BondSubmissionRequest, - wallet: &Wallet, -) -> Result { - // Deserialize the signed bond hex - let tx: Transaction = deserialize(&hex::decode(&bond_submission.signed_bond_hex)?)?; - - // Verify the transaction (this example assumes you've implemented your own verification logic) - let is_valid = verify_psbt(&tx, &wallet, &bond_submission)?; - if !is_valid { - return Err(anyhow!("Invalid PSBT")); - } - - // Create the response (you may need additional logic to generate order_id_hex and timestamp) - // let response = OrderActivatedResponse { - // order_id_hex: generate_order_id(&tx)?, // Assuming you have a function to generate this - // bond_locked_until_timestamp: calculate_bond_lock_time()?, // Assuming you have a function for this - // }; - - Ok(response) -} - -pub fn verify_psbt( - tx: &Transaction, - wallet: &Wallet, - bond_submission: &BondSubmissionRequest, -) -> Result { - // Example verification logic - // Check if the payout address matches - // let payout_address = bond_submission.payout_address.parse(); - // let output = tx.output.iter().find(|output| outputvc - // .script_pubkey == payout_address.script_pubkey()); - // if output.is_none() { - // return Ok(false); - // } - - // Check if the transaction is signed correctly - let mut psbt = PartiallySignedTransaction::from_unsigned_tx(tx.clone())?; - let finalized = wallet.sign(&mut psbt, SignOptions::default())?; - if !finalized { - return Ok(false); - } - - // Validate MuSig data (assuming you have methods for this) - let musig_data_valid = validate_musig_data( - &bond_submission.musig_pubkey_hex, - &bond_submission.musig_pub_nonce_hex, - )?; - if !musig_data_valid { - return Ok(false); - } - - Ok(true) -} - -fn generate_order_id(tx: &Transaction) -> Result { - // Example logic to generate an order ID from the transaction - Ok(tx.txid().to_string()) -} - -fn calculate_bond_lock_time() -> Result { - // Example logic to calculate the bond lock time - // This might depend on the current block height or a specific timestamp - Ok(12345678901234567890) // Placeholder value -} - -fn validate_musig_data(pubkey_hex: &str, nonce_hex: &str) -> Result { - // Example logic to validate MuSig data - // This might involve parsing the hex strings and ensuring they match expected values - Ok(true) // Placeholder validation -} diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index 43bdf0b..8bcadec 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -1,6 +1,7 @@ pub mod create_taproot; // pub mod mempool_actor; pub mod mempool_monitoring; -pub mod monitoring; // commented out for testing +pub mod monitoring; +pub mod tx_confirmation_monitoring; // commented out for testing use super::*; diff --git a/taptrade-cli-demo/coordinator/src/coordinator/tx_confirmation_monitoring.rs b/taptrade-cli-demo/coordinator/src/coordinator/tx_confirmation_monitoring.rs new file mode 100644 index 0000000..6d5b34a --- /dev/null +++ b/taptrade-cli-demo/coordinator/src/coordinator/tx_confirmation_monitoring.rs @@ -0,0 +1,79 @@ +use std::str::FromStr; + +use bdk::{bitcoin::Txid, bitcoincore_rpc::RpcApi}; + +use super::*; + +fn get_confirmations( + unconfirmed_txids: Vec, + coordinator: Arc, +) -> Result> { + let mut now_confirmed_txs = Vec::new(); + for txid in unconfirmed_txids { + let txid_struct = Txid::from_str(&txid)?; + let tx_info = coordinator + .coordinator_wallet + .json_rpc_client + .as_ref() + .get_raw_transaction_info(&txid_struct, None)?; + if let Some(confirmations) = tx_info.confirmations { + debug!( + "Transaction {} in now confirmed with {} confirmations", + &txid, confirmations + ); + if confirmations > 3 { + now_confirmed_txs.push(txid); + } + } + } + Ok(now_confirmed_txs) +} + +pub async fn update_transaction_confirmations(coordinator: Arc) { + loop { + tokio::time::sleep(std::time::Duration::from_secs(30)).await; + trace!("Checking for transaction confirmations"); + let unconfirmed_transactions = match coordinator + .coordinator_db + .fetch_unconfirmed_bond_txids() + .await + { + Ok(txids) => txids, + Err(e) => { + error!("Error fetching unconfirmed bond txids from db: {:?}", e); + tokio::time::sleep(std::time::Duration::from_secs(60)).await; + continue; + } + }; + if unconfirmed_transactions.is_empty() { + continue; + } + let coordinator_clone = Arc::clone(&coordinator); + let newly_confirmed_txids = match tokio::task::spawn_blocking(move || { + get_confirmations(unconfirmed_transactions, coordinator_clone) + }) + .await + { + Ok(result) => match result { + Ok(txids) => txids, + Err(e) => { + error!("Error getting confirmations: {:?}", e); + Vec::new() // or handle the error as appropriate + } + }, + Err(e) => { + error!("Getting tx confirmations spawn_blocking panicked: {:?}", e); + Vec::new() // or handle the error as appropriate + } + }; + if !newly_confirmed_txids.is_empty() { + if let Err(e) = coordinator + .coordinator_db + .confirm_bond_txids(newly_confirmed_txids) + .await + { + error!("Error updating bond confirmations in db: {:?}", e); + } + } + } +} diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index e77e47b..60f0daf 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -52,7 +52,7 @@ fn bool_to_sql_int(flag: bool) -> Option { impl CoordinatorDB { // will either create a new db or load existing one. Will create according tables in new db pub async fn init() -> Result { - dbg!(env::var("DATABASE_PATH")?); + debug!("coordinator db path: {}", env::var("DATABASE_PATH")?); let db_path = env::var("DATABASE_PATH").context("Parsing DATABASE_PATH from .env failed")?; @@ -121,8 +121,10 @@ impl CoordinatorDB { musig_pub_nonce_hex_taker TEXT NOT NULL, musig_pubkey_hex_taker TEXT NOT NULL, escrow_psbt_hex_maker TEXT, - escrow_psbt_hex_taker TEXT - )", + escrow_psbt_hex_taker TEXT, + escrow_psbt_txid TEXT, + escrow_psbt_is_confirmed INTEGER + )", // escrow_psbt_is_confirmed will be set 1 once the escrow psbt is confirmed onchain ) .execute(&db_pool) .await?; @@ -335,6 +337,7 @@ impl CoordinatorDB { trade_and_taker_info: &OfferPsbtRequest, trade_contract_psbt_maker: &String, trade_contract_psbt_taker: &String, + trade_tx_txid: String, ) -> Result<()> { let public_offer = self .fetch_and_delete_offer_from_public_offers_table( @@ -345,9 +348,9 @@ impl CoordinatorDB { sqlx::query( "INSERT OR REPLACE INTO taken_offers (offer_id, robohash_maker, robohash_taker, is_buy_order, amount_sat, 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, musig_pub_nonce_hex_maker, musig_pubkey_hex_maker - musig_pub_nonce_hex_taker, musig_pubkey_hex_taker, escrow_psbt_hex_maker, escrow_psbt_hex_taker) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + bond_tx_hex_taker, payout_address_maker, payout_address_taker, musig_pub_nonce_hex_maker, musig_pubkey_hex_maker, + musig_pub_nonce_hex_taker, musig_pubkey_hex_taker, escrow_psbt_hex_maker, escrow_psbt_hex_taker, escrow_psbt_txid, escrow_psbt_is_confirmed) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) .bind(public_offer.offer_id) .bind(public_offer.robohash_maker) @@ -369,6 +372,8 @@ impl CoordinatorDB { .bind(trade_and_taker_info.trade_data.musig_pubkey_hex.clone()) .bind(trade_contract_psbt_maker.clone()) .bind(trade_contract_psbt_taker.clone()) + .bind(trade_tx_txid) + .bind(0) .execute(&*self.db_pool) .await?; @@ -508,4 +513,30 @@ impl CoordinatorDB { // .await?; Ok(()) } + + pub async fn fetch_unconfirmed_bond_txids(&self) -> Result> { + let mut txids = Vec::new(); + let mut rows = sqlx::query( + "SELECT escrow_psbt_txid FROM taken_offers WHERE escrow_psbt_is_confirmed = 0", + ) + .fetch(&*self.db_pool); + while let Some(row) = rows.next().await { + let row = row?; + let txid: String = row.get("escrow_psbt_txid"); + txids.push(txid); + } + Ok(txids) + } + + pub async fn confirm_bond_txids(&self, confirmed_txids: Vec) -> Result<()> { + for txid in confirmed_txids { + sqlx::query( + "UPDATE taken_offers SET escrow_psbt_is_confirmed = 1 WHERE escrow_psbt_txid = ?", + ) + .bind(txid) + .execute(&*self.db_pool) + .await?; + } + Ok(()) + } } diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 62b8db9..3d0e2dd 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -8,6 +8,7 @@ use bdk::sled; use communication::{api::*, api_server}; use coordinator::monitoring::monitor_bonds; use coordinator::monitoring::*; +use coordinator::tx_confirmation_monitoring::update_transaction_confirmations; use database::CoordinatorDB; use dotenv::dotenv; use log::{debug, error, info, trace, warn}; @@ -47,6 +48,8 @@ async fn main() -> Result<()> { } } }); + let coordinator_ref = Arc::clone(&coordinator); + tokio::spawn(async move { update_transaction_confirmations(coordinator_ref).await }); // Start the API server api_server(coordinator).await?; Ok(()) diff --git a/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs b/taptrade-cli-demo/coordinator/src/wallet/escrow_psbt.rs deleted file mode 100644 index e69de29..0000000