testing and fixing api communication [coordinator & maker]

This commit is contained in:
f321x
2024-07-08 12:27:29 +00:00
parent 648ed82c0c
commit 06dedd1b95
9 changed files with 78 additions and 38 deletions

View File

@ -4,3 +4,4 @@ DATABASE_PATH="./dbs/trades.db" # path to the coordinator sqlite database stori
BDK_DB_PATH="./dbs/bdk-wallet" # Path to the BDK Sled database (no .db postfix)
WALLET_XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"
PUNISHMENT_ENABLED=1 # enable punishment for misbehaving traders
PORT=9999 # port for the coordinator to listen on

View File

@ -5,10 +5,11 @@ use self::api::*;
use self::utils::*;
use super::*;
use crate::wallet::*;
use anyhow::Context;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
routing::post,
routing::{get, post},
Extension, Json, Router,
};
use rand::Rng;
@ -21,10 +22,11 @@ use tokio::net::TcpListener;
//
/// Handler function to process the received data
async fn receive_order(
Extension(database): Extension<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(order): Json<OrderRequest>,
) -> Result<Json<BondRequirementResponse>, AppError> {
dbg!(&order);
if order.sanity_check().is_err() {
return Err(AppError(anyhow!("Invalid order request")));
}
@ -42,10 +44,11 @@ async fn receive_order(
/// receives the maker bond, verifies it and moves to offer to the active table (orderbook)
async fn submit_maker_bond(
Extension(database): Extension<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<BondSubmissionRequest>,
) -> Result<Response, AppError> {
println!("\n\nReceived maker bond: {:?}", payload);
let bond_requirements = if let Ok(requirements) = database
.fetch_bond_requirements(&payload.robohash_hex)
.await
@ -65,13 +68,24 @@ async fn submit_maker_bond(
return Ok(StatusCode::NOT_ACCEPTABLE.into_response());
}
}
println!("\nBond validation successful");
let offer_id_hex: String = generate_random_order_id(16); // 16 bytes random offer id, maybe a different system makes more sense later on? (uuid or increasing counter...)
// create address for taker bond
let new_taker_bond_address = wallet.get_new_address().await?;
let new_taker_bond_address = wallet.get_new_address().await.context(format!(
"Error generating taker bond address for offer id: {}",
offer_id_hex
))?;
// insert bond into sql database and move offer to different table
let bond_locked_until_timestamp = database
let bond_locked_until_timestamp = match database
.move_offer_to_active(&payload, &offer_id_hex, new_taker_bond_address)
.await?;
.await
{
Ok(timestamp) => timestamp,
Err(e) => {
dbg!("Error in validate_bond_tx_hex: {}", e);
return Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response());
}
};
// Create the JSON response
Ok(Json(OrderActivatedResponse {
@ -83,7 +97,7 @@ async fn submit_maker_bond(
/// returns available offers from the active table (orderbook)
async fn fetch_available_offers(
Extension(database): Extension<CoordinatorDB>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Json(payload): Json<OffersRequest>,
) -> Result<Json<PublicOffers>, AppError> {
let offers: Option<Vec<PublicOffer>> = database.fetch_suitable_offers(&payload).await?;
@ -94,8 +108,8 @@ async fn fetch_available_offers(
/// receives the taker bond for a given offer, verifies it, creates escrow transaction psbt
/// and moves the offer to the taken table. Will return the trade contract psbt for the taker to sign.
async fn submit_taker_bond(
Extension(database): Extension<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<OfferPsbtRequest>,
) -> Result<Response, AppError> {
let bond_requirements = database
@ -133,7 +147,7 @@ async fn submit_taker_bond(
/// gets polled by the maker and returns the escrow psbt in case the offer has been taken
async fn request_offer_status_maker(
Extension(database): Extension<CoordinatorDB>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
let offer = database
@ -153,8 +167,8 @@ async fn request_offer_status_maker(
/// coordinator then has to check if their signatures are valid and everything else is according to the agreed upon contract.
/// Once the coordinator has received both partitial signed PSBTs he can assemble them together to a transaction and publish it to the bitcoin network.
async fn submit_escrow_psbt(
Extension(database): Extension<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<PsbtSubmissionRequest>,
) -> Result<Response, AppError> {
panic!("implement")
@ -166,16 +180,16 @@ async fn submit_escrow_psbt(
/// 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<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
panic!("implement")
}
async fn submit_obligation_confirmation(
Extension(database): Extension<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
panic!("implement")
@ -186,18 +200,23 @@ async fn submit_obligation_confirmation(
/// 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<CoordinatorDB>,
Extension(wallet): Extension<CoordinatorWallet>,
Extension(database): Extension<Arc<CoordinatorDB>>,
Extension(wallet): Extension<Arc<CoordinatorWallet>>,
Json(payload): Json<OfferTakenRequest>,
) -> Result<Response, AppError> {
panic!("implement")
}
async fn test_api() -> &'static str {
"Hello, World!"
}
pub async fn api_server(coordinator: Arc<Coordinator>) -> Result<()> {
let database = Arc::clone(&coordinator.coordinator_db);
let wallet = Arc::clone(&coordinator.coordinator_wallet);
let app = Router::new()
.route("/test", get(test_api))
.route("/create-offer", post(receive_order))
.route("/submit-maker-bond", post(submit_maker_bond))
.route("/fetch-available-offers", post(fetch_available_offers))
@ -214,11 +233,13 @@ pub async fn api_server(coordinator: Arc<Coordinator>) -> Result<()> {
.layer(Extension(wallet));
// add other routes here
// Run the server on localhost:9999
let addr = SocketAddr::from(([127, 0, 0, 1], 9999));
let port: u16 = env::var("PORT")
.unwrap_or_else(|_| "9999".to_string())
.parse()?;
println!("Listening on {}", port);
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let tcp = TcpListener::bind(&addr).await.unwrap();
axum::serve(tcp, app).await?;
println!("Listening on {}", addr);
Ok(())
}

View File

@ -1,4 +1,4 @@
pub mod monitoring;
pub mod create_taproot;
// pub mod create_taproot; // commented out for testing
use super::*;

View File

@ -1,6 +1,7 @@
use super::*;
#[cfg(test)]
use anyhow::Ok;
async fn create_coordinator() -> Result<database::CoordinatorDB, anyhow::Error> {
// Set up the in-memory database
env::set_var("DATABASE_PATH", ":memory:");

View File

@ -1,5 +1,5 @@
ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server
COORDINATOR_ENDPOINT="http://127.0.0.1:3000"
COORDINATOR_ENDPOINT="http://127.0.0.1:9999"
ROBOHASH_HEX="26ee3dee4815655d223c3505162fd4610294a9542f89bb3d3e9748f534ac10ae" # sha256 of "robot21"
TRADE_TYPE="buy"
PAYOUT_ADDRESS="tb1p45daj2eaza6drcd85c3wvn0zrpqxuduk3rzcmla4eu7a02cep9kqjzkc64"

View File

@ -33,7 +33,7 @@ pub struct BondSubmissionRequest {
// Response after step2 if offer creation was successful and the offer is now online in the orderbook
#[derive(Debug, Deserialize)]
pub struct OrderActivatedResponse {
pub order_id_hex: String,
pub offer_id_hex: String,
pub bond_locked_until_timestamp: u64, // unix timestamp. Do not touch bond till then unless offer gets taken.
}

View File

@ -41,15 +41,20 @@ impl BondRequirementResponse {
pub fn fetch(trader_setup: &TraderSettings) -> Result<BondRequirementResponse> {
let client = reqwest::blocking::Client::new();
let endpoint = format!("{}{}", trader_setup.coordinator_endpoint, "/create-offer");
let res = client
.post(format!(
"{}{}",
trader_setup.coordinator_endpoint, "/create-offer"
))
.post(endpoint)
.json(&Self::_format_request(trader_setup))
.send()?
.json::<BondRequirementResponse>()?;
Ok(res)
.send()?;
let status_code = res.status();
match res.json::<BondRequirementResponse>() {
Ok(response) => Ok(response),
Err(e) => Err(anyhow!(
"Error fetching bond requirements: {}. Status code: {}",
e,
status_code
)),
}
}
}
@ -89,9 +94,21 @@ impl BondSubmissionRequest {
trader_setup.coordinator_endpoint, "/submit-maker-bond"
))
.json(&request)
.send()?
.json::<OrderActivatedResponse>()?;
Ok(res)
.send();
match res {
Ok(res) => {
let status_code = res.status();
match res.json::<OrderActivatedResponse>() {
Ok(response) => Ok(response),
Err(e) => Err(anyhow!(
"Error submitting maker bond: {}. Status code: {}",
e,
status_code
)),
}
}
Err(e) => Err(anyhow!("Error submitting maker bond: {}", e)),
}
}
}

View File

@ -24,7 +24,7 @@ impl ActiveOffer {
maker_config,
)?;
Ok(ActiveOffer {
offer_id_hex: submission_result.order_id_hex,
offer_id_hex: submission_result.offer_id_hex,
used_musig_config: musig_data,
used_bond: bond,
expected_payout_address: payout_address,

View File

@ -1,5 +1,5 @@
ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server
COORDINATOR_ENDPOINT="http://127.0.0.1:3000"
COORDINATOR_ENDPOINT="http://127.0.0.1:9999"
ROBOHASH_HEX="169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d" # sha256 of "robot22"
TRADE_TYPE="sell"
PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm"