From c1083e0cc04664634504c4a610ef22d21f588660 Mon Sep 17 00:00:00 2001 From: f321x Date: Thu, 4 Jul 2024 10:47:36 +0000 Subject: [PATCH] make chain backend async in coordinator --- taptrade-cli-demo/coordinator/.env | 2 + taptrade-cli-demo/coordinator/Cargo.lock | 209 ++++++++++++++++-- taptrade-cli-demo/coordinator/Cargo.toml | 2 +- .../coordinator/src/communication/mod.rs | 5 +- .../coordinator/src/coordinator/monitoring.rs | 42 +++- taptrade-cli-demo/coordinator/src/main.rs | 15 +- .../coordinator/src/wallet/mod.rs | 22 +- 7 files changed, 256 insertions(+), 41 deletions(-) diff --git a/taptrade-cli-demo/coordinator/.env b/taptrade-cli-demo/coordinator/.env index 17c2a81..470bf9e 100644 --- a/taptrade-cli-demo/coordinator/.env +++ b/taptrade-cli-demo/coordinator/.env @@ -1,4 +1,6 @@ ELECTRUM_BACKEND="ssl://mempool.space:40002" # we need a node with large mempool size limit for monitoring to miss no bond transactions +ESPLORA_BACKEND="https://blockstream.info/testnet/api" # blockstream.info testnet backend DATABASE_PATH="./dbs/trades.db" # path to the coordinator sqlite database storing the trades 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 diff --git a/taptrade-cli-demo/coordinator/Cargo.lock b/taptrade-cli-demo/coordinator/Cargo.lock index 78454db..9ee6047 100644 --- a/taptrade-cli-demo/coordinator/Cargo.lock +++ b/taptrade-cli-demo/coordinator/Cargo.lock @@ -84,10 +84,10 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "itoa", "matchit", @@ -117,8 +117,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -179,6 +179,8 @@ dependencies = [ "bitcoin", "bitcoinconsensus", "electrum-client", + "esplora-client", + "futures", "getrandom", "js-sys", "log", @@ -222,6 +224,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9997f8650dd818369931b5672a18dbef95324d0513aa99aae758de8ce86e5b" + [[package]] name = "bitcoin-private" version = "0.1.0" @@ -319,7 +327,7 @@ dependencies = [ "futures-util", "hex", "rand", - "reqwest", + "reqwest 0.12.5", "serde", "sqlx", "tokio", @@ -497,6 +505,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "esplora-client" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb1f7f2489cce83bc3bd92784f9ba5271eeb6e729b975895fc541f78cbfcdca" +dependencies = [ + "bitcoin", + "bitcoin-internals", + "log", + "reqwest 0.11.27", + "serde", +] + [[package]] name = "etcetera" version = "0.8.0" @@ -571,6 +592,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -644,6 +680,7 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -691,6 +728,25 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.5" @@ -702,7 +758,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -783,6 +839,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -794,6 +861,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -801,7 +879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -812,8 +890,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -829,6 +907,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.3.1" @@ -838,9 +940,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -857,8 +959,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", - "http", - "hyper", + "http 1.1.0", + "hyper 1.3.1", "hyper-util", "rustls 0.23.10", "rustls-pki-types", @@ -875,7 +977,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -892,9 +994,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", @@ -1424,6 +1526,43 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-socks", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + [[package]] name = "reqwest" version = "0.12.5" @@ -1436,11 +1575,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -1465,7 +1604,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -2192,6 +2331,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +dependencies = [ + "either", + "futures-util", + "thiserror", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -2649,6 +2800,16 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/taptrade-cli-demo/coordinator/Cargo.toml b/taptrade-cli-demo/coordinator/Cargo.toml index 5a31ea4..7bd1762 100644 --- a/taptrade-cli-demo/coordinator/Cargo.toml +++ b/taptrade-cli-demo/coordinator/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0.86" axum = { version = "0.7.5", features = ["tokio", "json"] } -bdk = { version = "0.29.0", features = ["key-value-db", "bitcoinconsensus", "verify"] } +bdk = { version = "0.29.0", features = ["key-value-db", "bitcoinconsensus", "verify", "use-esplora-async"] } dotenv = "0.15.0" futures-util = "0.3.30" hex = "0.4.3" diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index 4fc9705..d6cc558 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -184,7 +184,10 @@ async fn poll_final_payout( panic!("implement") } -pub async fn api_server(database: CoordinatorDB, wallet: CoordinatorWallet) -> Result<()> { +pub async fn api_server(coordinator: Coordinator) -> Result<()> { + let database = coordinator.coordinator_db; + let wallet = coordinator.coordinator_wallet; + let app = Router::new() .route("/create-offer", post(receive_order)) .route("/submit-maker-bond", post(submit_maker_bond)) diff --git a/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs b/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs index 8b399e0..c161336 100644 --- a/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs +++ b/taptrade-cli-demo/coordinator/src/coordinator/monitoring.rs @@ -16,10 +16,30 @@ pub struct MonitoringBond { pub table: Table, } -pub async fn monitor_bonds( - coordinator_db: &CoordinatorDB, - coordinator_wallet: &CoordinatorWallet, +// the current implementation only publishes the bond and removes the offer from the db +// in a more advanced implementation we could increase the transaction fee (cpfp) and +// continue monitoring the bond transaction until a confirmation happens for maximum pain +// in case the trader is actively malicious and did not just accidentally invalidate the bond +// we could directly forward bond sats to the other parties payout address in case it is a taken trade +async fn punish_trader( + coordinator: &Coordinator, + robohash: Vec, + bond: MonitoringBond, ) -> Result<()> { + // publish bond + coordinator + .coordinator_wallet + .publish_bond_tx_hex(bond_tx_hex) + .await?; + + // remove offer from db/orderbook + Ok(()) +} + +pub async fn monitor_bonds(coordinator: &Coordinator) -> Result<()> { + let coordinator_db = &coordinator.coordinator_db; + let coordinator_wallet = &coordinator.coordinator_wallet; + loop { // fetch all bonds let bonds = coordinator_db.fetch_all_bonds().await?; @@ -30,8 +50,20 @@ pub async fn monitor_bonds( .validate_bond_tx_hex(&bond.1.bond_tx_hex, &bond.1.requirements) .await { - // punish the violator (publish bond, remove offer from db/orderbook) - panic!("Implement bond violation punishment logic: {:?}", e); + match env::var("PUNISHMENT_ENABLED") + .unwrap_or_else(|_| "0".to_string()) + .as_str() + { + "1" => { + dbg!("Punishing trader for bond violation: {:?}", e); + punish_trader(coordinator, bond.0, bond.1).await?; + } + "0" => { + dbg!("Punishment disabled, ignoring bond violation: {:?}", e); + continue; + } + _ => Err(anyhow!("Invalid PUNISHMENT_ENABLED env var"))?, + } } } diff --git a/taptrade-cli-demo/coordinator/src/main.rs b/taptrade-cli-demo/coordinator/src/main.rs index 8f6db87..c1412d3 100755 --- a/taptrade-cli-demo/coordinator/src/main.rs +++ b/taptrade-cli-demo/coordinator/src/main.rs @@ -14,19 +14,26 @@ use std::{env, sync::Arc}; use tokio::sync::Mutex; use wallet::*; +pub struct Coordinator { + pub coordinator_db: CoordinatorDB, + pub coordinator_wallet: CoordinatorWallet, +} + // populate .env with values before starting #[tokio::main] async fn main() -> Result<()> { dotenv().ok(); // Initialize the database pool - let coordinator_db = CoordinatorDB::init().await?; - let wallet = CoordinatorWallet::init()?; + let coordinator = Coordinator { + coordinator_db: CoordinatorDB::init().await?, + coordinator_wallet: CoordinatorWallet::init()?, + }; // begin monitoring bonds - monitor_bonds(&coordinator_db, &wallet).await?; + monitor_bonds(&coordinator).await?; // Start the API server - api_server(coordinator_db, wallet).await?; + api_server(coordinator).await?; Ok(()) } diff --git a/taptrade-cli-demo/coordinator/src/wallet/mod.rs b/taptrade-cli-demo/coordinator/src/wallet/mod.rs index dd3e9d2..09b8e03 100644 --- a/taptrade-cli-demo/coordinator/src/wallet/mod.rs +++ b/taptrade-cli-demo/coordinator/src/wallet/mod.rs @@ -4,8 +4,9 @@ use super::*; use anyhow::Context; use bdk::{ bitcoin::{self, bip32::ExtendedPrivKey, consensus::encode::deserialize, Transaction}, - blockchain::ElectrumBlockchain, + blockchain::{Blockchain, ElectrumBlockchain, EsploraBlockchain}, electrum_client::Client, + esplora_client::AsyncClient, sled::{self, Tree}, template::Bip86, wallet::verify::*, @@ -18,7 +19,7 @@ use utils::*; #[derive(Clone)] pub struct CoordinatorWallet { pub wallet: Arc>>, - pub backend: Arc, + pub backend: Arc, } #[derive(PartialEq, Debug)] @@ -33,10 +34,11 @@ impl CoordinatorWallet { let wallet_xprv = ExtendedPrivKey::from_str( &env::var("WALLET_XPRV").context("loading WALLET_XPRV from .env failed")?, )?; - let backend = ElectrumBlockchain::from(Client::new( - &env::var("ELECTRUM_BACKEND") - .context("Parsing ELECTRUM_BACKEND from .env failed, is it set?")?, - )?); + // let backend = ElectrumBlockchain::from(Client::new( + // &env::var("ELECTRUM_BACKEND") + // .context("Parsing ELECTRUM_BACKEND from .env failed, is it set?")?, + // )?); + let backend = EsploraBlockchain::new(&env::var("ESPLORA_BACKEND")?, 1000); let sled_db = sled::open(env::var("BDK_DB_PATH")?)?.open_tree("default_wallet")?; let wallet = Wallet::new( Bip86(wallet_xprv, KeychainKind::External), @@ -110,6 +112,14 @@ impl CoordinatorWallet { } Ok(()) } + + pub async fn publish_bond_tx_hex(&self, bond: &str) -> Result<()> { + let blockchain = &*self.backend; + let tx: Transaction = deserialize(&hex::decode(bond)?)?; + + blockchain.broadcast(&tx).await?; + Ok(()) + } } impl fmt::Debug for CoordinatorWallet {