complete more api endpoints

This commit is contained in:
f321x
2024-07-19 17:25:15 +02:00
parent ff556bdc4f
commit 5a59ba63b0
3 changed files with 99 additions and 17 deletions

View File

@ -160,7 +160,7 @@ async fn request_offer_status_maker(
Json(payload): Json<OfferTakenRequest>, Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> { ) -> Result<Response, AppError> {
let offer = database let offer = database
.fetch_taken_offer_maker(&payload.order_id_hex, &payload.robohash_hex) .fetch_taken_offer_maker(&payload.offer_id_hex, &payload.robohash_hex)
.await?; .await?;
match offer { match offer {
Some(offer) => Ok(Json(OfferTakenResponse { Some(offer) => Ok(Json(OfferTakenResponse {
@ -196,13 +196,13 @@ async fn poll_escrow_confirmation(
Json(payload): Json<OfferTakenRequest>, Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> { ) -> Result<Response, AppError> {
if !database if !database
.is_valid_robohash_in_table(&payload.robohash_hex, &payload.order_id_hex) .is_valid_robohash_in_table(&payload.robohash_hex, &payload.offer_id_hex)
.await? .await?
{ {
return Ok(StatusCode::NOT_FOUND.into_response()); return Ok(StatusCode::NOT_FOUND.into_response());
} }
if database if database
.fetch_escrow_tx_confirmation_status(&payload.order_id_hex) .fetch_escrow_tx_confirmation_status(&payload.offer_id_hex)
.await? .await?
{ {
return Ok(StatusCode::OK.into_response()); return Ok(StatusCode::OK.into_response());
@ -213,20 +213,19 @@ async fn poll_escrow_confirmation(
async fn submit_obligation_confirmation( async fn submit_obligation_confirmation(
Extension(database): Extension<Arc<CoordinatorDB>>, Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet<sled::Tree>>>,
Json(payload): Json<OfferTakenRequest>, Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> { ) -> Result<Response, AppError> {
// sanity check if offer is in table and if the escrow tx is confirmed // sanity check if offer is in table and if the escrow tx is confirmed
if !database if !database
.is_valid_robohash_in_table(&payload.robohash_hex, &payload.order_id_hex) .is_valid_robohash_in_table(&payload.robohash_hex, &payload.offer_id_hex)
.await? || !database .await? || !database
.fetch_escrow_tx_confirmation_status(&payload.order_id_hex) .fetch_escrow_tx_confirmation_status(&payload.offer_id_hex)
.await? .await?
{ {
return Ok(StatusCode::NOT_FOUND.into_response()); return Ok(StatusCode::NOT_FOUND.into_response());
} }
database database
.set_trader_happy_field(&payload.order_id_hex, &payload.robohash_hex, true) .set_trader_happy_field(&payload.offer_id_hex, &payload.robohash_hex, true)
.await?; .await?;
Ok(StatusCode::OK.into_response()) Ok(StatusCode::OK.into_response())
} }
@ -271,15 +270,44 @@ async fn poll_final_payout(
{ {
return Ok(StatusCode::NOT_FOUND.into_response()); return Ok(StatusCode::NOT_FOUND.into_response());
} }
// check if both traders are happy
// assemble payout psbt and return to them for signing
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 // 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) // 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 // 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 // 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
panic!("implement") // 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
}
} else {
return Ok(StatusCode::PROCESSING.into_response()); // this will be returned if the coordinator hasn't decided yet
}
} }
async fn test_api() -> &'static str { async fn test_api() -> &'static str {

View File

@ -40,6 +40,12 @@ struct AwaitingTakerOffer {
musig_pubkey_hex_maker: String, musig_pubkey_hex_maker: String,
} }
pub struct TraderHappiness {
pub maker_happy: Option<bool>,
pub taker_happy: Option<bool>,
pub escrow_ongoing: bool,
}
fn bool_to_sql_int(flag: bool) -> Option<i64> { fn bool_to_sql_int(flag: bool) -> Option<i64> {
if flag { if flag {
Some(1) Some(1)
@ -126,6 +132,8 @@ impl CoordinatorDB {
escrow_psbt_is_confirmed INTEGER, escrow_psbt_is_confirmed INTEGER,
maker_happy INTEGER, maker_happy INTEGER,
taker_happy INTEGER, taker_happy INTEGER,
escrow_ongoing INTEGER NOT NULL,
escrow_winner_robohash TEXT
)", // escrow_psbt_is_confirmed will be set 1 once the escrow psbt is confirmed onchain )", // escrow_psbt_is_confirmed will be set 1 once the escrow psbt is confirmed onchain
) )
.execute(&db_pool) .execute(&db_pool)
@ -351,8 +359,8 @@ impl CoordinatorDB {
"INSERT OR REPLACE INTO taken_offers (offer_id, robohash_maker, robohash_taker, is_buy_order, amount_sat, "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_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, 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) musig_pub_nonce_hex_taker, musig_pubkey_hex_taker, escrow_psbt_hex_maker, escrow_psbt_hex_taker, escrow_psbt_txid, escrow_psbt_is_confirmed, escrow_ongoing)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
) )
.bind(public_offer.offer_id) .bind(public_offer.offer_id)
.bind(public_offer.robohash_maker) .bind(public_offer.robohash_maker)
@ -376,6 +384,7 @@ impl CoordinatorDB {
.bind(trade_contract_psbt_taker.clone()) .bind(trade_contract_psbt_taker.clone())
.bind(trade_tx_txid) .bind(trade_tx_txid)
.bind(0) .bind(0)
.bind(0)
.execute(&*self.db_pool) .execute(&*self.db_pool)
.await?; .await?;
@ -613,6 +622,44 @@ impl CoordinatorDB {
.execute(&*self.db_pool) .execute(&*self.db_pool)
.await?; .await?;
if !is_happy {
sqlx::query("UPDATE taken_offers SET escrow_ongoing = 1 WHERE offer_id = ?")
.bind(offer_id)
.execute(&*self.db_pool)
.await?;
}
Ok(()) Ok(())
} }
pub async fn fetch_trader_happiness(&self, offer_id: &String) -> Result<TraderHappiness> {
let row = sqlx::query(
"SELECT maker_happy, taker_happy, escrow_ongoing FROM taken_offers WHERE offer_id = ?",
)
.bind(offer_id)
.fetch_one(&*self.db_pool)
.await?;
let maker_happy: Option<i64> = row.try_get::<Option<i64>, _>("maker_happy")?;
let taker_happy: Option<i64> = row.try_get::<Option<i64>, _>("taker_happy")?;
let escrow_ongoing: i64 = row.try_get::<i64, _>("escrow_ongoing")?;
Ok(TraderHappiness {
maker_happy: maker_happy.map(|v| v != 0),
taker_happy: taker_happy.map(|v| v != 0),
escrow_ongoing: escrow_ongoing != 0,
})
}
pub async fn fetch_escrow_result(&self, offer_id: &String) -> 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)
.await?;
let winner_robohash: Option<String> =
row.try_get::<Option<String>, _>("escrow_winner_robohash")?;
Ok(winner_robohash)
}
} }

View File

@ -15,7 +15,7 @@ use bdk::{
wallet::AddressInfo, wallet::AddressInfo,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{str::FromStr, thread::sleep, time::Duration}; use std::{f32::consts::E, str::FromStr, thread::sleep, time::Duration};
impl BondRequirementResponse { impl BondRequirementResponse {
fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { fn _format_request(trader_setup: &TraderSettings) -> OrderRequest {
@ -257,13 +257,20 @@ impl IsOfferReadyRequest {
.send()?; .send()?;
if res.status() == 200 { if res.status() == 200 {
// good case, psbt is returned // good case, psbt is returned
debug!("Payout psbt received. Signing...");
break; break;
} else if res.status() == 204 { } else if res.status() == 202 {
// still waiting, retry // still waiting, retry
continue; continue;
} else if res.status() == 201 { } else if res.status() == 102 {
// other party initiated escrow // other party initiated escrow
return Ok(None); debug!("Other party initiated escrow. Waiting for coordinator to finalize.");
continue;
} else if res.status() != 410 {
return Err(anyhow!(
"We lost the escrow, your bond is gone: {}",
res.status()
));
} else { } else {
// unintended response // unintended response
return Err(anyhow!( return Err(anyhow!(