mirror of
https://github.com/RoboSats/taptrade-core.git
synced 2025-12-28 23:59:21 +00:00
make chain backend async in coordinator
This commit is contained in:
@ -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
|
||||
|
||||
209
taptrade-cli-demo/coordinator/Cargo.lock
generated
209
taptrade-cli-demo/coordinator/Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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<u8>,
|
||||
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"))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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<Mutex<Wallet<Tree>>>,
|
||||
pub backend: Arc<ElectrumBlockchain>,
|
||||
pub backend: Arc<EsploraBlockchain>,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
||||
Reference in New Issue
Block a user