Merge branch 'research' of github.com:RoboSats/taptrade-core into research

This commit is contained in:
aaravm
2024-07-24 16:37:14 +05:30
12 changed files with 312 additions and 203 deletions

View File

@ -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<E> From<E> for AppError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}

View File

@ -0,0 +1,25 @@
#[derive(Debug)]
pub enum BondError {
InvalidBond(String),
BondNotFound,
CoordinatorError(String),
}
#[derive(Debug)]
pub enum FetchOffersError {
NoOffersAvailable,
Database(String),
}
#[derive(Debug)]
pub enum FetchEscrowConfirmationError {
NotFound,
Database(String),
}
#[derive(Debug)]
pub enum RequestError {
Database(String),
NotConfirmed,
NotFound,
}

View File

@ -1,10 +1,9 @@
pub mod api;
mod utils;
pub mod communication_utils;
pub mod handler_errors;
use self::api::*;
use self::utils::*;
use self::communication_utils::*;
use super::*;
use crate::wallet::*;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
@ -24,10 +23,10 @@ async fn receive_order(
Json(offer): Json<OfferRequest>,
) -> Result<Response, AppError> {
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())
}
}
@ -42,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())
}
}
}
@ -65,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())
}
@ -84,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())
}
}
}
@ -107,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())
}
@ -140,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())
}
@ -152,22 +151,24 @@ async fn poll_escrow_confirmation(
}
async fn submit_obligation_confirmation(
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(coordinator): Extension<Arc<Coordinator>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
// 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::NotFound) => {
info!("Offer for obligation confirmation not found");
Ok(StatusCode::NOT_FOUND.into_response())
}
Err(RequestError::NotConfirmed) => {
info!("Offer for obligation confirmation not confirmed");
Ok(StatusCode::NOT_ACCEPTABLE.into_response())
}
Err(RequestError::Database(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<Arc<CoordinatorDB>>,
Extension(coordinator): Extension<Arc<Coordinator>>,
Json(payload): Json<TradeObligationsUnsatisfied>,
) -> Result<Response, AppError> {
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::NotConfirmed) => {
info!("Offer tx for escrow initiation not confirmed");
Ok(StatusCode::NOT_ACCEPTABLE.into_response())
}
Err(RequestError::NotFound) => {
info!("Offer for escrow initiation not found");
Ok(StatusCode::NOT_FOUND.into_response())
}
Err(RequestError::Database(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<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet<sled::Tree>>>,
Extension(coordinator): Extension<Arc<Coordinator>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
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::NotConfirmed) => {
info!("Offer tx for final payout not confirmed");
Ok(StatusCode::NOT_ACCEPTABLE.into_response())
}
Err(RequestError::NotFound) => {
info!("Offer for final payout not found");
Ok(StatusCode::NOT_FOUND.into_response())
}
Err(RequestError::Database(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
}
}
@ -283,31 +257,3 @@ pub async fn api_server(coordinator: Arc<Coordinator>) -> 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<E> From<E> for AppError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}

View File

@ -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(())
}

View File

@ -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)]

View File

@ -0,0 +1,46 @@
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<u8> = rand::thread_rng()
.sample_iter(&rand::distributions::Standard)
.take(len)
.collect();
// Convert bytes to hex string
hex::encode(bytes)
}
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::NotFound),
Ok(true) => (),
Err(e) => return Err(RequestError::Database(e.to_string())),
};
// sanity check if the escrow tx is confirmed
match database
.fetch_escrow_tx_confirmation_status(offer_id_hex)
.await
{
Ok(false) => Err(RequestError::NotConfirmed),
Ok(true) => Ok(()),
Err(e) => Err(RequestError::Database(e.to_string())),
}
}

View File

@ -50,7 +50,7 @@ fn run_mempool(mempool: Arc<Mempool>) {
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) => {

View File

@ -1,31 +1,12 @@
pub mod bond_monitoring;
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::*;
#[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 async fn process_order(
coordinator: Arc<Coordinator>,
offer: &OfferRequest,
@ -89,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,
@ -116,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() {
@ -159,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,
@ -186,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 {
@ -207,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
@ -217,11 +198,101 @@ 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)
}
}
pub async fn handle_obligation_confirmation(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
) -> Result<(), RequestError> {
let database = &coordinator.coordinator_db;
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::Database(e.to_string()));
}
Ok(())
}
pub async fn initiate_escrow(
payload: &TradeObligationsUnsatisfied,
coordinator: Arc<Coordinator>,
) -> Result<(), RequestError> {
let database = &coordinator.coordinator_db;
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::Database(e.to_string()));
}
Ok(())
}
pub async fn handle_final_payout(
payload: &OfferTakenRequest,
coordinator: Arc<Coordinator>,
) -> Result<PayoutProcessingResult, RequestError> {
let database = &coordinator.coordinator_db;
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::Database(e.to_string())),
};
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
// .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::Database(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
Ok(PayoutProcessingResult::LostEscrow)
}
} else {
// this will be returned if the coordinator hasn't decided yet
Ok(PayoutProcessingResult::DecidingEscrow)
}
}

View File

@ -1,13 +0,0 @@
use super::*;
pub fn generate_random_order_id(len: usize) -> String {
// Generate `len` random bytes
let bytes: Vec<u8> = rand::thread_rng()
.sample_iter(&rand::distributions::Standard)
.take(len)
.collect();
// Convert bytes to hex string
let hex_string = hex::encode(bytes);
hex_string
}

View File

@ -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<u64> {
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<BondRequirements> {
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
@ -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)
@ -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<Option<String>> {
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<bool> {
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<bool> {
pub async fn fetch_escrow_tx_confirmation_status(&self, offer_id: &str) -> Result<bool> {
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<TraderHappiness> {
pub async fn fetch_trader_happiness(&self, offer_id: &str) -> Result<TraderHappiness> {
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<Option<String>> {
pub async fn fetch_escrow_result(&self, offer_id: &str) -> Result<Option<String>> {
let row = sqlx::query("SELECT escrow_winner_robohash FROM taken_offers WHERE offer_id = ?")
.bind(offer_id)
.fetch_one(&*self.db_pool)

View File

@ -5,15 +5,20 @@ mod wallet;
use anyhow::{anyhow, Result};
use bdk::sled;
use communication::{api::*, api_server, *};
use coordinator::tx_confirmation_monitoring::update_transaction_confirmations;
use coordinator::{monitoring::*, *};
use communication::{api::*, api_server, communication_utils::*, handler_errors::*};
use coordinator::{
bond_monitoring::*, coordinator_utils::*,
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::*;
@ -32,6 +37,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 +55,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(())

View File

@ -80,7 +80,7 @@ pub async fn init_coordinator_wallet() -> Result<CoordinatorWallet<sled::Tree>>
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<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
{
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<D: bdk::database::BatchDatabase> CoordinatorWallet<D> {
}
// 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(