From efaa843a1af261638e74950a0b05090b8d147c86 Mon Sep 17 00:00:00 2001 From: f321x Date: Sun, 21 Jul 2024 13:41:47 +0200 Subject: [PATCH 1/3] cleanup api and separate logic --- .../src/communication/handler_errors.rs | 25 ++++ .../coordinator/src/communication/mod.rs | 122 +++++++---------- .../coordinator/src/coordinator/mod.rs | 124 +++++++++++++++--- .../coordinator/src/coordinator/utils.rs | 26 ++++ .../coordinator/src/database/mod.rs | 26 ++-- taptrade-cli-demo/coordinator/src/main.rs | 6 +- 6 files changed, 224 insertions(+), 105 deletions(-) create mode 100644 taptrade-cli-demo/coordinator/src/communication/handler_errors.rs diff --git a/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs b/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs new file mode 100644 index 0000000..84a1116 --- /dev/null +++ b/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs @@ -0,0 +1,25 @@ +#[derive(Debug)] +pub enum BondError { + InvalidBond(String), + BondNotFound, + CoordinatorError(String), +} + +#[derive(Debug)] +pub enum FetchOffersError { + NoOffersAvailable, + DatabaseError(String), +} + +#[derive(Debug)] +pub enum FetchEscrowConfirmationError { + NotFoundError, + DatabaseError(String), +} + +#[derive(Debug)] +pub enum RequestError { + DatabaseError(String), + NotConfirmedError, + NotFoundError, +} diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index 8924bd2..84aa77a 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -1,10 +1,9 @@ pub mod api; +pub mod handler_errors; mod utils; -use self::api::*; use self::utils::*; use super::*; -use crate::wallet::*; use axum::{ http::StatusCode, response::{IntoResponse, Response}, @@ -152,22 +151,24 @@ async fn poll_escrow_confirmation( } async fn submit_obligation_confirmation( - Extension(database): Extension>, + Extension(coordinator): 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.offer_id_hex) - .await? || !database - .fetch_escrow_tx_confirmation_status(&payload.offer_id_hex) - .await? - { - return Ok(StatusCode::NOT_FOUND.into_response()); + match handle_obligation_confirmation(&payload, coordinator).await { + Ok(_) => Ok(StatusCode::OK.into_response()), + Err(RequestError::NotFoundError) => { + info!("Offer for obligation confirmation not found"); + Ok(StatusCode::NOT_FOUND.into_response()) + } + Err(RequestError::NotConfirmedError) => { + info!("Offer for obligation confirmation not confirmed"); + Ok(StatusCode::NOT_ACCEPTABLE.into_response()) + } + Err(RequestError::DatabaseError(e)) => { + error!("Database error fetching obligation confirmation: {e}"); + Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) + } } - database - .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, true) - .await?; - Ok(StatusCode::OK.into_response()) } // or @@ -175,22 +176,24 @@ async fn submit_obligation_confirmation( // 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(coordinator): Extension>, Json(payload): Json, ) -> Result { - if !database - .is_valid_robohash_in_table(&payload.robohash_hex, &payload.offer_id_hex) - .await? || !database - .fetch_escrow_tx_confirmation_status(&payload.offer_id_hex) - .await? - { - return Ok(StatusCode::NOT_FOUND.into_response()); + match initiate_escrow(&payload, coordinator).await { + Ok(_) => Ok(StatusCode::OK.into_response()), + Err(RequestError::NotConfirmedError) => { + info!("Offer tx for escrow initiation not confirmed"); + Ok(StatusCode::NOT_ACCEPTABLE.into_response()) + } + Err(RequestError::NotFoundError) => { + info!("Offer for escrow initiation not found"); + Ok(StatusCode::NOT_FOUND.into_response()) + } + Err(RequestError::DatabaseError(e)) => { + error!("Database error fetching obligation confirmation: {e}"); + Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) + } } - database - .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, false) - .await?; - - Ok(StatusCode::OK.into_response()) } /// Is supposed to get polled by the traders once they clicked on "i sent the fiat" or "i received the fiat". @@ -198,55 +201,26 @@ async fn request_escrow( /// If one of them is not happy and initiating escrow (e.g. claiming they didn't receive the fiat) then this /// endpoint can return 201 and the escrow mediation logic will get executed (tbd). async fn poll_final_payout( - Extension(database): Extension>, - Extension(wallet): Extension>>, + Extension(coordinator): Extension>, Json(payload): Json, ) -> Result { - if !database - .is_valid_robohash_in_table(&payload.robohash_hex, &payload.offer_id_hex) - .await? || !database - .fetch_escrow_tx_confirmation_status(&payload.offer_id_hex) - .await? - { - return Ok(StatusCode::NOT_FOUND.into_response()); - } - - let trader_happiness = database - .fetch_trader_happiness(&payload.offer_id_hex) - .await?; - if trader_happiness.maker_happy.is_some_and(|x| x == true) - && trader_happiness.taker_happy.is_some_and(|x| x == true) - { - panic!("Implement wallet.assemble_keyspend_payout_psbt()"); - // let payout_keyspend_psbt_hex = wallet - // .assemble_keyspend_payout_psbt(&payload.offer_id_hex, &payload.robohash_hex) - // .await - // .context("Error assembling payout PSBT")?; - // return Ok(String::from(payout_keyspend_psbt_hex).into_response()); - } else if (trader_happiness.maker_happy.is_none() || trader_happiness.taker_happy.is_none()) - && !trader_happiness.escrow_ongoing - { - return Ok(StatusCode::ACCEPTED.into_response()); - } - // if one of them is not happy - // open escrow cli on coordinator to decide who will win (chat/dispute is out of scope for this demo) - // once decided who will win assemble the correct payout psbt and return it to the according trader - // the other trader gets a error code/ end of trade code - // escrow winner has to be set true with a cli input of the coordinator. This could be an api - // endpoint for the admin UI frontend in the future - if let Some(escrow_winner) = database.fetch_escrow_result(&payload.offer_id_hex).await? { - if escrow_winner == payload.robohash_hex { - panic!("Implement wallet.assemble_script_payout_psbt()"); - // let script_payout_psbt_hex = wallet - // .assemble_script_payout_psbt(&payload.offer_id_hex, &payload.robohash_hex, is_maker_bool) - // .await - // .context("Error assembling payout PSBT")?; - // return Ok(String::from(payout_keyspend_psbt_hex).into_response()); - } else { - return Ok(StatusCode::GONE.into_response()); // this will be returned to the losing trader + match handle_final_payout(&payload, coordinator).await { + Ok(PayoutProcessingResult::NotReady) => Ok(StatusCode::ACCEPTED.into_response()), + Ok(PayoutProcessingResult::LostEscrow) => Ok(StatusCode::GONE.into_response()), + Ok(PayoutProcessingResult::ReadyPSBT(psbt)) => Ok(psbt.into_response()), + Ok(PayoutProcessingResult::DecidingEscrow) => Ok(StatusCode::PROCESSING.into_response()), + Err(RequestError::NotConfirmedError) => { + info!("Offer tx for final payout not confirmed"); + Ok(StatusCode::NOT_ACCEPTABLE.into_response()) + } + Err(RequestError::NotFoundError) => { + info!("Offer for final payout not found"); + Ok(StatusCode::NOT_FOUND.into_response()) + } + Err(RequestError::DatabaseError(e)) => { + error!("Database error fetching final payout: {e}"); + Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } - } else { - return Ok(StatusCode::PROCESSING.into_response()); // this will be returned if the coordinator hasn't decided yet } } diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index ab3078a..ad52285 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -7,23 +7,11 @@ pub mod utils; use self::utils::*; use super::*; -#[derive(Debug)] -pub enum BondError { - InvalidBond(String), - BondNotFound, - CoordinatorError(String), -} - -#[derive(Debug)] -pub enum FetchOffersError { - NoOffersAvailable, - DatabaseError(String), -} - -#[derive(Debug)] -pub enum FetchEscrowConfirmationError { - NotFoundError, - DatabaseError(String), +pub enum PayoutProcessingResult { + ReadyPSBT(String), + NotReady, + LostEscrow, + DecidingEscrow, } pub async fn process_order( @@ -225,3 +213,105 @@ pub async fn fetch_escrow_confirmation_status( Err(FetchEscrowConfirmationError::NotFoundError) } } + +pub async fn handle_obligation_confirmation( + payload: &OfferTakenRequest, + coordinator: Arc, +) -> Result<(), RequestError> { + let database = &coordinator.coordinator_db; + + if let Err(e) = + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await + { + return Err(e); + } + if let Err(e) = database + .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, true) + .await + { + return Err(RequestError::DatabaseError(e.to_string())); + } + Ok(()) +} + +pub async fn initiate_escrow( + payload: &TradeObligationsUnsatisfied, + coordinator: Arc, +) -> Result<(), RequestError> { + let database = &coordinator.coordinator_db; + + if let Err(e) = + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await + { + return Err(e); + } + + if let Err(e) = database + .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, false) + .await + { + return Err(RequestError::DatabaseError(e.to_string())); + } + + Ok(()) +} + +pub async fn handle_final_payout( + payload: &OfferTakenRequest, + coordinator: Arc, +) -> Result { + let database = &coordinator.coordinator_db; + + if let Err(e) = + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await + { + return Err(e); + } + + let trader_happiness = match database.fetch_trader_happiness(&payload.offer_id_hex).await { + Ok(happiness) => happiness, + Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + }; + + if trader_happiness.maker_happy.is_some_and(|x| x == true) + && trader_happiness.taker_happy.is_some_and(|x| x == true) + { + panic!("Implement wallet.assemble_keyspend_payout_psbt()"); + // let payout_keyspend_psbt_hex = wallet + // .assemble_keyspend_payout_psbt(&payload.offer_id_hex, &payload.robohash_hex) + // .await + // .context("Error assembling payout PSBT")?; + // return Ok(PayoutProcessingResult::ReadyPSBT(payout_keyspend_psbt_hex)); + } else if (trader_happiness.maker_happy.is_none() || trader_happiness.taker_happy.is_none()) + && !trader_happiness.escrow_ongoing + { + return Ok(PayoutProcessingResult::NotReady); + } + // if one of them is not happy + // open escrow cli on coordinator to decide who will win (chat/dispute is out of scope for this demo) + // once decided who will win assemble the correct payout psbt and return it to the according trader + // the other trader gets a error code/ end of trade code + // escrow winner has to be set true with a cli input of the coordinator. This could be an api + // endpoint for the admin UI frontend in the future + let potential_escrow_winner = match database.fetch_escrow_result(&payload.offer_id_hex).await { + Ok(escrow_winner) => escrow_winner, + Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + }; + + if let Some(escrow_winner) = potential_escrow_winner { + if escrow_winner == payload.robohash_hex { + panic!("Implement wallet.assemble_script_payout_psbt()"); + // let script_payout_psbt_hex = wallet + // .assemble_script_payout_psbt(&payload.offer_id_hex, &payload.robohash_hex, is_maker_bool) + // .await + // .context("Error assembling payout PSBT")?; + // return Ok(PayoutProcessingResult::ReadyPSBT(script_payout_psbt_hex)); + } else { + // this will be returned to the losing trader + return Ok(PayoutProcessingResult::LostEscrow); + } + } else { + // this will be returned if the coordinator hasn't decided yet + return Ok(PayoutProcessingResult::DecidingEscrow); + } +} diff --git a/taptrade-cli-demo/coordinator/src/coordinator/utils.rs b/taptrade-cli-demo/coordinator/src/coordinator/utils.rs index 544a8ed..2b28911 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/utils.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/utils.rs @@ -11,3 +11,29 @@ pub fn generate_random_order_id(len: usize) -> String { let hex_string = hex::encode(bytes); hex_string } + +pub async fn check_offer_and_confirmation( + offer_id_hex: &str, + robohash_hex: &str, + database: &CoordinatorDB, +) -> Result<(), RequestError> { + // sanity check if offer is in table + match database + .is_valid_robohash_in_table(robohash_hex, offer_id_hex) + .await + { + Ok(false) => return Err(RequestError::NotFoundError), + Ok(true) => (), + Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + }; + + // sanity check if the escrow tx is confirmed + match database + .fetch_escrow_tx_confirmation_status(offer_id_hex) + .await + { + Ok(false) => return Err(RequestError::NotConfirmedError), + Ok(true) => Ok(()), + Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + } +} diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index 5d738ae..a6cf6c5 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -218,7 +218,7 @@ impl CoordinatorDB { pub async fn move_offer_to_active( &self, data: &BondSubmissionRequest, - offer_id: &String, + offer_id: &str, taker_bond_address: String, ) -> Result { let remaining_offer_information = self @@ -291,7 +291,7 @@ impl CoordinatorDB { pub async fn fetch_taker_bond_requirements( &self, - offer_id_hex: &String, + offer_id_hex: &str, ) -> Result { let taker_bond_requirements = sqlx::query( "SELECT taker_bond_address, bond_amount_sat, amount_sat FROM active_maker_offers WHERE offer_id = ?", @@ -345,8 +345,8 @@ impl CoordinatorDB { pub async fn add_taker_info_and_move_table( &self, trade_and_taker_info: &OfferPsbtRequest, - trade_contract_psbt_maker: &String, - trade_contract_psbt_taker: &String, + trade_contract_psbt_maker: &str, + trade_contract_psbt_taker: &str, trade_tx_txid: String, ) -> Result<()> { let public_offer = self @@ -393,8 +393,8 @@ impl CoordinatorDB { pub async fn fetch_taken_offer_maker( &self, - offer_id_hex: &String, - robohash_hex_maker: &String, + offer_id_hex: &str, + robohash_hex_maker: &str, ) -> Result> { let offer = sqlx::query( "SELECT escrow_psbt_hex_maker, robohash_maker FROM taken_offers WHERE offer_id = ?", @@ -563,8 +563,8 @@ impl CoordinatorDB { pub async fn is_valid_robohash_in_table( &self, - robohash_hex: &String, - offer_id: &String, + robohash_hex: &str, + offer_id: &str, ) -> Result { let robohash = hex::decode(robohash_hex)?; let robohash = sqlx::query( @@ -578,7 +578,7 @@ impl CoordinatorDB { Ok(robohash.is_some()) } - pub async fn fetch_escrow_tx_confirmation_status(&self, offer_id: &String) -> Result { + pub async fn fetch_escrow_tx_confirmation_status(&self, offer_id: &str) -> Result { let status = sqlx::query("SELECT escrow_psbt_is_confirmed FROM taken_offers WHERE offer_id = ?") .bind(offer_id) @@ -589,8 +589,8 @@ impl CoordinatorDB { pub async fn set_trader_happy_field( &self, - offer_id: &String, - robohash: &String, + offer_id: &str, + robohash: &str, is_happy: bool, ) -> Result<()> { let robohash_bytes = hex::decode(robohash)?; @@ -632,7 +632,7 @@ impl CoordinatorDB { Ok(()) } - pub async fn fetch_trader_happiness(&self, offer_id: &String) -> Result { + pub async fn fetch_trader_happiness(&self, offer_id: &str) -> Result { let row = sqlx::query( "SELECT maker_happy, taker_happy, escrow_ongoing FROM taken_offers WHERE offer_id = ?", ) @@ -651,7 +651,7 @@ impl CoordinatorDB { }) } - pub async fn fetch_escrow_result(&self, offer_id: &String) -> Result> { + pub async fn fetch_escrow_result(&self, offer_id: &str) -> Result> { let row = sqlx::query("SELECT escrow_winner_robohash FROM taken_offers WHERE offer_id = ?") .bind(offer_id) .fetch_one(&*self.db_pool) diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 4d56080..5fbb662 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -5,7 +5,7 @@ mod wallet; use anyhow::{anyhow, Result}; use bdk::sled; -use communication::{api::*, api_server, *}; +use communication::{api::*, api_server, handler_errors::*, *}; use coordinator::tx_confirmation_monitoring::update_transaction_confirmations; use coordinator::{monitoring::*, *}; use database::CoordinatorDB; @@ -32,6 +32,7 @@ async fn main() -> Result<()> { .init(); dotenv().ok(); debug!("Starting coordinator"); + // Initialize the database pool let coordinator = Arc::new(Coordinator { coordinator_db: Arc::new(CoordinatorDB::init().await?), @@ -49,8 +50,11 @@ async fn main() -> Result<()> { } } }); + + // begin monitoring escrow transactions confirmations 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(()) From c8bc01e856c9123768524073b63b957007c268bd Mon Sep 17 00:00:00 2001 From: f321x Date: Sun, 21 Jul 2024 14:28:38 +0200 Subject: [PATCH 2/3] cargo clippy --- .../src/communication/communication_utils.rs | 46 ++++++++++++ .../src/communication/handler_errors.rs | 12 +-- .../coordinator/src/communication/mod.rs | 74 ++++++------------- .../coordinator/src/communication/utils.rs | 18 ----- .../{utils.rs => coordinator_utils.rs} | 19 +++-- .../src/coordinator/mempool_monitoring.rs | 2 +- .../coordinator/src/coordinator/mod.rs | 61 ++++++--------- .../coordinator/src/coordinator/monitoring.rs | 1 - .../coordinator/src/database/mod.rs | 4 +- taptrade-cli-demo/coordinator/src/main.rs | 15 ++-- .../coordinator/src/wallet/mod.rs | 6 +- 11 files changed, 124 insertions(+), 134 deletions(-) create mode 100644 taptrade-cli-demo/coordinator/src/communication/communication_utils.rs delete mode 100644 taptrade-cli-demo/coordinator/src/communication/utils.rs rename taptrade-cli-demo/coordinator/src/coordinator/{utils.rs => coordinator_utils.rs} (66%) diff --git a/taptrade-cli-demo/coordinator/src/communication/communication_utils.rs b/taptrade-cli-demo/coordinator/src/communication/communication_utils.rs new file mode 100644 index 0000000..c2d0de8 --- /dev/null +++ b/taptrade-cli-demo/coordinator/src/communication/communication_utils.rs @@ -0,0 +1,46 @@ +use super::*; + +pub fn validate_timestamp(offer_duration_ts: u64) -> Result<(), ValidationError> { + // Get the current time + let now = SystemTime::now(); + // Convert the current time to a UNIX timestamp + let unix_timestamp = now + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + if offer_duration_ts < unix_timestamp + 10800 { + return Err(ValidationError::new("Offer duration too short")); + } + if offer_duration_ts > unix_timestamp + 604800 { + return Err(ValidationError::new("Offer duration too long")); + } + Ok(()) +} + +// ANYHOW ERROR HANDLING +// -------------- +// Make our own error that wraps `anyhow::Error`. +#[derive(Debug)] +pub struct AppError(anyhow::Error); + +// Tell axum how to convert `AppError` into a response. +impl IntoResponse for AppError { + fn into_response(self) -> Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} diff --git a/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs b/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs index 84a1116..7df1a8f 100644 --- a/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs +++ b/taptrade-cli-demo/coordinator/src/communication/handler_errors.rs @@ -8,18 +8,18 @@ pub enum BondError { #[derive(Debug)] pub enum FetchOffersError { NoOffersAvailable, - DatabaseError(String), + Database(String), } #[derive(Debug)] pub enum FetchEscrowConfirmationError { - NotFoundError, - DatabaseError(String), + NotFound, + Database(String), } #[derive(Debug)] pub enum RequestError { - DatabaseError(String), - NotConfirmedError, - NotFoundError, + Database(String), + NotConfirmed, + NotFound, } diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index 84aa77a..0dfab83 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -1,8 +1,8 @@ pub mod api; +pub mod communication_utils; pub mod handler_errors; -mod utils; -use self::utils::*; +use self::communication_utils::*; use super::*; use axum::{ http::StatusCode, @@ -23,10 +23,10 @@ async fn receive_order( Json(offer): Json, ) -> Result { if let Err(_) = offer.validate() { - return Ok(StatusCode::BAD_REQUEST.into_response()); + Ok(StatusCode::BAD_REQUEST.into_response()) } else { let bond_requirements = process_order(coordinator, &offer).await?; - return Ok(Json(bond_requirements).into_response()); + Ok(Json(bond_requirements).into_response()) } } @@ -41,15 +41,15 @@ async fn submit_maker_bond( Ok(offer_activated_response) => Ok(Json(offer_activated_response).into_response()), Err(BondError::BondNotFound) => { info!("Bond requirements not found in database"); - return Ok(StatusCode::NOT_FOUND.into_response()); + Ok(StatusCode::NOT_FOUND.into_response()) } Err(BondError::InvalidBond(e)) => { warn!("Invalid bond submission: {e}"); - return Ok(StatusCode::NOT_ACCEPTABLE.into_response()); + Ok(StatusCode::NOT_ACCEPTABLE.into_response()) } Err(BondError::CoordinatorError(e)) => { error!("Coordinator error on bond submission: {e}"); - return Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()); + Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } } } @@ -64,7 +64,7 @@ async fn fetch_available_offers( match get_public_offers(&payload, coordinator).await { Ok(offers) => Ok(Json(offers).into_response()), Err(FetchOffersError::NoOffersAvailable) => Ok(StatusCode::NO_CONTENT.into_response()), - Err(FetchOffersError::DatabaseError(e)) => { + Err(FetchOffersError::Database(e)) => { error!("Database error fetching offers: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -83,15 +83,15 @@ async fn submit_taker_bond( Ok(offer_taken_response) => Ok(Json(offer_taken_response).into_response()), Err(BondError::BondNotFound) => { info!("Bond requirements not found in database"); - return Ok(StatusCode::NOT_FOUND.into_response()); + Ok(StatusCode::NOT_FOUND.into_response()) } Err(BondError::InvalidBond(e)) => { warn!("Invalid bond submission: {e}"); - return Ok(StatusCode::NOT_ACCEPTABLE.into_response()); + Ok(StatusCode::NOT_ACCEPTABLE.into_response()) } Err(BondError::CoordinatorError(e)) => { error!("Coordinator error on bond submission: {e}"); - return Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()); + Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } } } @@ -106,7 +106,7 @@ async fn request_offer_status_maker( match get_offer_status_maker(&payload, coordinator).await { Ok(offer_taken_response) => Ok(Json(offer_taken_response).into_response()), Err(FetchOffersError::NoOffersAvailable) => Ok(StatusCode::NO_CONTENT.into_response()), - Err(FetchOffersError::DatabaseError(e)) => { + Err(FetchOffersError::Database(e)) => { error!("Database error fetching offers: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -139,11 +139,11 @@ async fn poll_escrow_confirmation( match fetch_escrow_confirmation_status(&payload, coordinator).await { Ok(true) => Ok(StatusCode::OK.into_response()), Ok(false) => Ok(StatusCode::ACCEPTED.into_response()), - Err(FetchEscrowConfirmationError::NotFoundError) => { + Err(FetchEscrowConfirmationError::NotFound) => { info!("Escrow confirmation check transaction not found"); Ok(StatusCode::NOT_FOUND.into_response()) } - Err(FetchEscrowConfirmationError::DatabaseError(e)) => { + Err(FetchEscrowConfirmationError::Database(e)) => { error!("Database error fetching escrow confirmation: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -156,15 +156,15 @@ async fn submit_obligation_confirmation( ) -> Result { match handle_obligation_confirmation(&payload, coordinator).await { Ok(_) => Ok(StatusCode::OK.into_response()), - Err(RequestError::NotFoundError) => { + Err(RequestError::NotFound) => { info!("Offer for obligation confirmation not found"); Ok(StatusCode::NOT_FOUND.into_response()) } - Err(RequestError::NotConfirmedError) => { + Err(RequestError::NotConfirmed) => { info!("Offer for obligation confirmation not confirmed"); Ok(StatusCode::NOT_ACCEPTABLE.into_response()) } - Err(RequestError::DatabaseError(e)) => { + Err(RequestError::Database(e)) => { error!("Database error fetching obligation confirmation: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -181,15 +181,15 @@ async fn request_escrow( ) -> Result { match initiate_escrow(&payload, coordinator).await { Ok(_) => Ok(StatusCode::OK.into_response()), - Err(RequestError::NotConfirmedError) => { + Err(RequestError::NotConfirmed) => { info!("Offer tx for escrow initiation not confirmed"); Ok(StatusCode::NOT_ACCEPTABLE.into_response()) } - Err(RequestError::NotFoundError) => { + Err(RequestError::NotFound) => { info!("Offer for escrow initiation not found"); Ok(StatusCode::NOT_FOUND.into_response()) } - Err(RequestError::DatabaseError(e)) => { + Err(RequestError::Database(e)) => { error!("Database error fetching obligation confirmation: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -209,15 +209,15 @@ async fn poll_final_payout( Ok(PayoutProcessingResult::LostEscrow) => Ok(StatusCode::GONE.into_response()), Ok(PayoutProcessingResult::ReadyPSBT(psbt)) => Ok(psbt.into_response()), Ok(PayoutProcessingResult::DecidingEscrow) => Ok(StatusCode::PROCESSING.into_response()), - Err(RequestError::NotConfirmedError) => { + Err(RequestError::NotConfirmed) => { info!("Offer tx for final payout not confirmed"); Ok(StatusCode::NOT_ACCEPTABLE.into_response()) } - Err(RequestError::NotFoundError) => { + Err(RequestError::NotFound) => { info!("Offer for final payout not found"); Ok(StatusCode::NOT_FOUND.into_response()) } - Err(RequestError::DatabaseError(e)) => { + Err(RequestError::Database(e)) => { error!("Database error fetching final payout: {e}"); Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response()) } @@ -257,31 +257,3 @@ pub async fn api_server(coordinator: Arc) -> Result<()> { Ok(()) } - -// ANYHOW ERROR HANDLING -// -------------- -// Make our own error that wraps `anyhow::Error`. -#[derive(Debug)] -pub struct AppError(anyhow::Error); - -// Tell axum how to convert `AppError` into a response. -impl IntoResponse for AppError { - fn into_response(self) -> Response { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Something went wrong: {}", self.0), - ) - .into_response() - } -} - -// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into -// `Result<_, AppError>`. That way you don't need to do that manually. -impl From for AppError -where - E: Into, -{ - fn from(err: E) -> Self { - Self(err.into()) - } -} diff --git a/taptrade-cli-demo/coordinator/src/communication/utils.rs b/taptrade-cli-demo/coordinator/src/communication/utils.rs deleted file mode 100644 index 45f4642..0000000 --- a/taptrade-cli-demo/coordinator/src/communication/utils.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::*; - -pub fn validate_timestamp(offer_duration_ts: u64) -> Result<(), ValidationError> { - // Get the current time - let now = SystemTime::now(); - // Convert the current time to a UNIX timestamp - let unix_timestamp = now - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - if offer_duration_ts < unix_timestamp + 10800 { - return Err(ValidationError::new("Offer duration too short")); - } - if offer_duration_ts > unix_timestamp + 604800 { - return Err(ValidationError::new("Offer duration too long")); - } - Ok(()) -} diff --git a/taptrade-cli-demo/coordinator/src/coordinator/utils.rs b/taptrade-cli-demo/coordinator/src/coordinator/coordinator_utils.rs similarity index 66% rename from taptrade-cli-demo/coordinator/src/coordinator/utils.rs rename to taptrade-cli-demo/coordinator/src/coordinator/coordinator_utils.rs index 2b28911..6f2b163 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/utils.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/coordinator_utils.rs @@ -1,5 +1,13 @@ use super::*; +#[derive(Debug)] +pub enum PayoutProcessingResult { + ReadyPSBT(String), + NotReady, + LostEscrow, + DecidingEscrow, +} + pub fn generate_random_order_id(len: usize) -> String { // Generate `len` random bytes let bytes: Vec = rand::thread_rng() @@ -8,8 +16,7 @@ pub fn generate_random_order_id(len: usize) -> String { .collect(); // Convert bytes to hex string - let hex_string = hex::encode(bytes); - hex_string + hex::encode(bytes) } pub async fn check_offer_and_confirmation( @@ -22,9 +29,9 @@ pub async fn check_offer_and_confirmation( .is_valid_robohash_in_table(robohash_hex, offer_id_hex) .await { - Ok(false) => return Err(RequestError::NotFoundError), + Ok(false) => return Err(RequestError::NotFound), Ok(true) => (), - Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + Err(e) => return Err(RequestError::Database(e.to_string())), }; // sanity check if the escrow tx is confirmed @@ -32,8 +39,8 @@ pub async fn check_offer_and_confirmation( .fetch_escrow_tx_confirmation_status(offer_id_hex) .await { - Ok(false) => return Err(RequestError::NotConfirmedError), + Ok(false) => Err(RequestError::NotConfirmed), Ok(true) => Ok(()), - Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + Err(e) => Err(RequestError::Database(e.to_string())), } } diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mempool_monitoring.rs b/taptrade-cli-demo/coordinator/src/coordinator/mempool_monitoring.rs index 01a0a4a..ccbd37f 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/mempool_monitoring.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mempool_monitoring.rs @@ -50,7 +50,7 @@ fn run_mempool(mempool: Arc) { let tx = match mempool .json_rpc_client .deref() - .get_raw_transaction(&txid, None) + .get_raw_transaction(txid, None) { std::result::Result::Ok(tx) => tx, Err(e) => { diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index ad52285..12d3aa7 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -1,19 +1,12 @@ +pub mod coordinator_utils; pub mod create_taproot; pub mod mempool_monitoring; pub mod monitoring; pub mod tx_confirmation_monitoring; -pub mod utils; -use self::utils::*; +use self::coordinator_utils::*; use super::*; -pub enum PayoutProcessingResult { - ReadyPSBT(String), - NotReady, - LostEscrow, - DecidingEscrow, -} - pub async fn process_order( coordinator: Arc, offer: &OfferRequest, @@ -77,7 +70,7 @@ pub async fn handle_maker_bond( }; // insert bond into sql database and move offer to different table let bond_locked_until_timestamp = match database - .move_offer_to_active(&payload, &offer_id_hex, new_taker_bond_address) + .move_offer_to_active(payload, &offer_id_hex, new_taker_bond_address) .await { Ok(timestamp) => timestamp, @@ -104,7 +97,7 @@ pub async fn get_public_offers( let offers = match database.fetch_suitable_offers(request).await { Ok(offers) => offers, Err(e) => { - return Err(FetchOffersError::DatabaseError(e.to_string())); + return Err(FetchOffersError::Database(e.to_string())); } }; if offers.is_none() { @@ -147,7 +140,7 @@ pub async fn handle_taker_bond( if let Err(e) = database .add_taker_info_and_move_table( - &payload, + payload, &trade_contract_psbt_maker, &trade_contract_psbt_taker, escrow_tx_txid, @@ -174,7 +167,7 @@ pub async fn get_offer_status_maker( { Ok(offer) => offer, Err(e) => { - return Err(FetchOffersError::DatabaseError(e.to_string())); + return Err(FetchOffersError::Database(e.to_string())); } }; match offer { @@ -195,9 +188,9 @@ pub async fn fetch_escrow_confirmation_status( .is_valid_robohash_in_table(&payload.robohash_hex, &payload.offer_id_hex) .await { - Ok(false) => return Err(FetchEscrowConfirmationError::NotFoundError), + Ok(false) => return Err(FetchEscrowConfirmationError::NotFound), Ok(true) => (), - Err(e) => return Err(FetchEscrowConfirmationError::DatabaseError(e.to_string())), + Err(e) => return Err(FetchEscrowConfirmationError::Database(e.to_string())), } if match database @@ -205,12 +198,12 @@ pub async fn fetch_escrow_confirmation_status( .await { Ok(status) => status, - Err(e) => return Err(FetchEscrowConfirmationError::DatabaseError(e.to_string())), + Err(e) => return Err(FetchEscrowConfirmationError::Database(e.to_string())), } { // rust smh Ok(true) } else { - Err(FetchEscrowConfirmationError::NotFoundError) + Err(FetchEscrowConfirmationError::NotFound) } } @@ -220,16 +213,12 @@ pub async fn handle_obligation_confirmation( ) -> Result<(), RequestError> { let database = &coordinator.coordinator_db; - if let Err(e) = - check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await - { - return Err(e); - } + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await?; if let Err(e) = database .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, true) .await { - return Err(RequestError::DatabaseError(e.to_string())); + return Err(RequestError::Database(e.to_string())); } Ok(()) } @@ -240,17 +229,13 @@ pub async fn initiate_escrow( ) -> Result<(), RequestError> { let database = &coordinator.coordinator_db; - if let Err(e) = - check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await - { - return Err(e); - } + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await?; if let Err(e) = database .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, false) .await { - return Err(RequestError::DatabaseError(e.to_string())); + return Err(RequestError::Database(e.to_string())); } Ok(()) @@ -262,19 +247,15 @@ pub async fn handle_final_payout( ) -> Result { let database = &coordinator.coordinator_db; - if let Err(e) = - check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await - { - return Err(e); - } + check_offer_and_confirmation(&payload.offer_id_hex, &payload.robohash_hex, database).await?; let trader_happiness = match database.fetch_trader_happiness(&payload.offer_id_hex).await { Ok(happiness) => happiness, - Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + Err(e) => return Err(RequestError::Database(e.to_string())), }; - if trader_happiness.maker_happy.is_some_and(|x| x == true) - && trader_happiness.taker_happy.is_some_and(|x| x == true) + if trader_happiness.maker_happy.is_some_and(|x| x) + && trader_happiness.taker_happy.is_some_and(|x| x) { panic!("Implement wallet.assemble_keyspend_payout_psbt()"); // let payout_keyspend_psbt_hex = wallet @@ -295,7 +276,7 @@ pub async fn handle_final_payout( // endpoint for the admin UI frontend in the future let potential_escrow_winner = match database.fetch_escrow_result(&payload.offer_id_hex).await { Ok(escrow_winner) => escrow_winner, - Err(e) => return Err(RequestError::DatabaseError(e.to_string())), + Err(e) => return Err(RequestError::Database(e.to_string())), }; if let Some(escrow_winner) = potential_escrow_winner { @@ -308,10 +289,10 @@ pub async fn handle_final_payout( // return Ok(PayoutProcessingResult::ReadyPSBT(script_payout_psbt_hex)); } else { // this will be returned to the losing trader - return Ok(PayoutProcessingResult::LostEscrow); + Ok(PayoutProcessingResult::LostEscrow) } } else { // this will be returned if the coordinator hasn't decided yet - return Ok(PayoutProcessingResult::DecidingEscrow); + Ok(PayoutProcessingResult::DecidingEscrow) } } diff --git a/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs b/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs index 18da9ef..525698c 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs @@ -4,7 +4,6 @@ // Also needs to implement punishment logic in case a fraud is detected. use super::*; use anyhow::Context; -use mempool_monitoring::MempoolHandler; use sha2::{Digest, Sha256}; #[derive(Debug, Clone, PartialEq)] diff --git a/taptrade-cli-demo/coordinator/src/database/mod.rs b/taptrade-cli-demo/coordinator/src/database/mod.rs index a6cf6c5..40509f5 100644 --- a/taptrade-cli-demo/coordinator/src/database/mod.rs +++ b/taptrade-cli-demo/coordinator/src/database/mod.rs @@ -380,8 +380,8 @@ 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(trade_contract_psbt_maker.clone()) - .bind(trade_contract_psbt_taker.clone()) + .bind(trade_contract_psbt_maker) + .bind(trade_contract_psbt_taker) .bind(trade_tx_txid) .bind(0) .bind(0) diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 5fbb662..8b7d47c 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -5,15 +5,20 @@ mod wallet; use anyhow::{anyhow, Result}; use bdk::sled; -use communication::{api::*, api_server, handler_errors::*, *}; -use coordinator::tx_confirmation_monitoring::update_transaction_confirmations; -use coordinator::{monitoring::*, *}; +use communication::{api::*, api_server, communication_utils::*, handler_errors::*}; +use coordinator::{ + coordinator_utils::*, monitoring::*, + tx_confirmation_monitoring::update_transaction_confirmations, *, +}; use database::CoordinatorDB; use dotenv::dotenv; use log::{debug, error, info, trace, warn}; use rand::Rng; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::{env, sync::Arc}; +use std::{ + env, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; use tokio::sync::Mutex; use validator::{Validate, ValidationError}; use wallet::*; diff --git a/taptrade-cli-demo/coordinator/src/wallet/mod.rs b/taptrade-cli-demo/coordinator/src/wallet/mod.rs index 35023ac..67254db 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/mod.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/mod.rs @@ -80,7 +80,7 @@ pub async fn init_coordinator_wallet() -> Result> Ok(CoordinatorWallet { wallet: Arc::new(Mutex::new(wallet)), backend: Arc::new(backend), - json_rpc_client: json_rpc_client, + json_rpc_client, mempool: Arc::new(mempool), }) } @@ -128,8 +128,6 @@ impl CoordinatorWallet { { let wallet = self.wallet.lock().await; for bond in bonds.as_ref().iter() { - let input_sum: u64; - let tx: Transaction = deserialize(&hex::decode(&bond.bond_tx_hex)?)?; debug!("Validating bond in validate_bonds()"); // we need to test this with signed and invalid/unsigned transactions @@ -140,7 +138,7 @@ impl CoordinatorWallet { } // check if the tx has the correct input amounts (have to be >= trading amount) - input_sum = match tx.input_sum(blockchain, &*wallet.database()) { + let input_sum: u64 = match tx.input_sum(blockchain, &*wallet.database()) { Ok(amount) => { if amount < bond.requirements.min_input_sum_sat { invalid_bonds.insert( From c34e60c39d61719cf5cd2bfcc464a0c21cffcab2 Mon Sep 17 00:00:00 2001 From: f321x Date: Sun, 21 Jul 2024 14:30:48 +0200 Subject: [PATCH 3/3] rename monitoring to bond_monitoring --- .../src/coordinator/{monitoring.rs => bond_monitoring.rs} | 0 taptrade-cli-demo/coordinator/src/coordinator/mod.rs | 2 +- taptrade-cli-demo/coordinator/src/main.rs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename taptrade-cli-demo/coordinator/src/coordinator/{monitoring.rs => bond_monitoring.rs} (100%) diff --git a/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs b/taptrade-cli-demo/coordinator/src/coordinator/bond_monitoring.rs similarity index 100% rename from taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs rename to taptrade-cli-demo/coordinator/src/coordinator/bond_monitoring.rs diff --git a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs index 12d3aa7..1f09068 100755 --- a/taptrade-cli-demo/coordinator/src/coordinator/mod.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/mod.rs @@ -1,7 +1,7 @@ +pub mod bond_monitoring; pub mod coordinator_utils; pub mod create_taproot; pub mod mempool_monitoring; -pub mod monitoring; pub mod tx_confirmation_monitoring; use self::coordinator_utils::*; diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 8b7d47c..eab5f48 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, Result}; use bdk::sled; use communication::{api::*, api_server, communication_utils::*, handler_errors::*}; use coordinator::{ - coordinator_utils::*, monitoring::*, + bond_monitoring::*, coordinator_utils::*, tx_confirmation_monitoring::update_transaction_confirmations, *, }; use database::CoordinatorDB;