diff --git a/taptrade-cli-demo/coordinator/src/communication/mod.rs b/taptrade-cli-demo/coordinator/src/communication/mod.rs index e7205a5..2374c7c 100755 --- a/taptrade-cli-demo/coordinator/src/communication/mod.rs +++ b/taptrade-cli-demo/coordinator/src/communication/mod.rs @@ -5,7 +5,7 @@ use tokio::net::TcpListener; #[derive(Deserialize, Serialize, Debug)] struct OrderRequest { - robohash_base91: String, + robohash_hex: String, amount_satoshi: u64, order_type: String, bond_ratio: u8, @@ -16,7 +16,7 @@ async fn receive_order(Json(order): Json) { println!("Received order: {:?}", order); // Access individual fields - let robohash = &order.robohash_base91; + let robohash = &order.robohash_hex; let amount = order.amount_satoshi; let order_type = &order.order_type; let bond_ratio = order.bond_ratio; @@ -31,7 +31,7 @@ async fn receive_order(Json(order): Json) { // Example of further processing if order_type == "buy" { println!("Processing a buy order..."); - // Add your buy order logic here + // Add your buy order logic here } else if order_type == "sell" { println!("Processing a sell order..."); // Add your sell order logic here diff --git a/taptrade-cli-demo/trader/Cargo.lock b/taptrade-cli-demo/trader/Cargo.lock index e479ec9..58db7d8 100644 --- a/taptrade-cli-demo/trader/Cargo.lock +++ b/taptrade-cli-demo/trader/Cargo.lock @@ -137,12 +137,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base91" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eb5fbae7b5ee422f239444a3dca9bdf5ecb3abf3af1bf87c8097db3f7bc025" - [[package]] name = "bdk" version = "0.29.0" @@ -575,6 +569,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hex_lit" version = "0.1.1" @@ -1586,10 +1586,10 @@ name = "trader" version = "0.1.0" dependencies = [ "anyhow", - "base91", "bdk", "dotenv", "env_logger", + "hex", "log", "musig2", "rand_core", diff --git a/taptrade-cli-demo/trader/Cargo.toml b/taptrade-cli-demo/trader/Cargo.toml index 3560033..41f664e 100644 --- a/taptrade-cli-demo/trader/Cargo.toml +++ b/taptrade-cli-demo/trader/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] anyhow = "1.0.86" -base91 = "0.1.0" bdk = "0.29.0" dotenv = "0.15.0" env_logger = "0.11.3" +hex = "0.4.3" log = "0.4.21" musig2 = "0.0.11" rand_core = "0.6.4" diff --git a/taptrade-cli-demo/trader/maker.env b/taptrade-cli-demo/trader/maker.env index b485306..46f9141 100644 --- a/taptrade-cli-demo/trader/maker.env +++ b/taptrade-cli-demo/trader/maker.env @@ -1,7 +1,8 @@ ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server COORDINATOR_ENDPOINT="http://127.0.0.1:3000" -ROBOHASH_BASE91="o!kfNkee;RF?m6FT87:1bjASE2/(1NFTnR;ILmHB<1p/vP%STz~IWolNDJa.#PmTI)sfBk+Gs!,(w8M" # "base91 of hex of sha256 of robot21" +ROBOHASH_HEX="26ee3dee4815655d223c3505162fd4610294a9542f89bb3d3e9748f534ac10ae" # sha256 of "robot21" TRADE_TYPE="buy" PAYOUT_ADDRESS="tb1p45daj2eaza6drcd85c3wvn0zrpqxuduk3rzcmla4eu7a02cep9kqjzkc64" BOND_RATIO=5 XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv +OFFER_DURATION_HOURS=48 diff --git a/taptrade-cli-demo/trader/src/cli/mod.rs b/taptrade-cli-demo/trader/src/cli/mod.rs index eb3f80f..e0e706e 100755 --- a/taptrade-cli-demo/trader/src/cli/mod.rs +++ b/taptrade-cli-demo/trader/src/cli/mod.rs @@ -1,10 +1,13 @@ -use anyhow::{anyhow, Result}; -use sha2::{Digest, Sha256}; -use std::env; -use std::io::{self, Write}; - use crate::wallet::get_wallet_xprv; +use anyhow::{anyhow, Result}; use bdk::bitcoin::bip32::ExtendedPrivKey; +use hex; +use sha2::{Digest, Sha256}; +use std::{ + env, + io::{self, Write}, + time::{SystemTime, UNIX_EPOCH}, +}; #[derive(Debug)] pub struct Coordinator; @@ -19,11 +22,12 @@ pub enum OfferType { pub struct TraderSettings { pub electrum_endpoint: String, pub coordinator_endpoint: String, - pub robosats_robohash_base91: String, + pub robosats_robohash_hex: String, pub trade_type: OfferType, pub payout_address: String, pub bond_ratio: u8, pub wallet_xprv: ExtendedPrivKey, + pub duration_unix_ts: u64, // until when the order should stay available } #[derive(Debug)] @@ -39,14 +43,6 @@ fn hash256(input: &String) -> [u8; 32] { hasher.finalize().into() } -// Robosats uses base91 encoded sha256 hash of the private robot key -fn bytes_to_base91(input: &[u8; 32]) -> String { - let encoded_robohash: String = base91::EncodeIterator::new(input.iter().copied()) - .as_char_iter() - .collect(); - encoded_robohash -} - impl OfferType { pub fn value(&self) -> u64 { match self { @@ -87,11 +83,17 @@ impl CliSettings { } } + // parses the hours input string and returns the unix timestamp + the trade duration in seconds + fn hours_to_ts(hours: &String) -> Result { + let duration: u64 = hours.parse()?; + Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() + duration * 3600) + } + fn get_trader_settings() -> Result { let electrum_endpoint = Self::get_user_input("Enter electrum endpoint: "); let coordinator_endpoint = Self::get_user_input("Enter coordinator endpoint: "); - let robosats_robohash_base91 = bytes_to_base91(&hash256(&Self::get_user_input( - "Enter your robosats robot key: ", + let robosats_robohash_hex = hex::encode(&hash256(&Self::get_user_input( + "Enter your robosats robot key: ", // just for testing purposes, to be improved to the real robohash spec ))); let trade_type: OfferType = Self::get_trade_type(None); let payout_address = Self::get_user_input( @@ -101,14 +103,18 @@ impl CliSettings { let wallet_xprv = Self::check_xprv_input(Some(Self::get_user_input( "Enter funded testnet wallet xprv or leave empty to generate: ", )))?; + let duration_unix_ts: u64 = Self::hours_to_ts(&Self::get_user_input( + "How many hours should the offer stay online: ", + ))?; Ok(TraderSettings { electrum_endpoint, coordinator_endpoint, - robosats_robohash_base91, + robosats_robohash_hex, trade_type, payout_address, bond_ratio, wallet_xprv, + duration_unix_ts, }) } @@ -126,11 +132,12 @@ impl CliSettings { Ok(TraderSettings { electrum_endpoint: env::var("ELECTRUM_ENDPOINT")?, coordinator_endpoint: env::var("COORDINATOR_ENDPOINT")?, - robosats_robohash_base91: env::var("ROBOHASH_BASE91")?, + robosats_robohash_hex: env::var("ROBOHASH_HEX")?, trade_type: Self::get_trade_type(Some(env::var("TRADE_TYPE")?)), payout_address: env::var("PAYOUT_ADDRESS")?, bond_ratio: env::var("BOND_RATIO")?.parse()?, wallet_xprv: Self::check_xprv_input(Some(env::var("XPRV")?))?, + duration_unix_ts: Self::hours_to_ts(&env::var("OFFER_DURATION_HOURS")?)?, }) } diff --git a/taptrade-cli-demo/trader/src/communication/api.rs b/taptrade-cli-demo/trader/src/communication/api.rs index 77ec6c1..400180b 100644 --- a/taptrade-cli-demo/trader/src/communication/api.rs +++ b/taptrade-cli-demo/trader/src/communication/api.rs @@ -1,24 +1,36 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize)] -pub struct OfferCreationResponse { - pub bond_address: String, - pub locking_amount: u64, // validate -} - +// maker step 1 +// requesting to create an offer on the orderbook (POST request) #[derive(Serialize)] pub struct OrderRequest { - pub robohash_base91: String, - pub amount_satoshi: u64, - pub order_type: String, // buy or sell - pub bond_ratio: u8, // [2, 50] + pub robohash_hex: String, // identifier of the trader + pub amount_satoshi: u64, // amount in satoshi to buy or sell + pub is_buy_order: bool, // true if buy, false if sell + pub bond_ratio: u8, // [2, 50]% of trading amount + pub offer_duration_ts: u64, // unix timestamp how long the offer should stay available } +// coordinator answer to maker step 1 +// direct Json answer to step 1 (same request) +#[derive(Debug, Deserialize)] +pub struct OfferCreationResponse { + pub bond_address: String, // address the bond has to be locked to + pub locking_amount_sat: u64, // min amount of the bond output in sat +} + +// maker step 2 +// (submission of signed bond and other data neccessary to coordinate the trade) #[derive(Serialize)] pub struct BondSubmissionRequest { - pub robohash_base91: String, - pub signed_bond_base91: String, - pub payout_address: String, // does this make sense here? - pub musig_pub_nonce_base91: String, - pub musig_pubkey_base91: String, + pub robohash_hex: String, + pub signed_bond_hex: String, // signed bond transaction, hex encoded + pub payout_address: String, // does this make sense here? + pub musig_pub_nonce_hex: String, + pub musig_pubkey_hex: String, +} + +pub struct OrderActivatedResponse { + pub order_id_hex: String, + pub bond_locked_until_timestamp: u128, // unix timestamp. Do not touch bond till then unless offer gets taken. } diff --git a/taptrade-cli-demo/trader/src/communication/mod.rs b/taptrade-cli-demo/trader/src/communication/mod.rs index 5e207f8..e5a21b2 100644 --- a/taptrade-cli-demo/trader/src/communication/mod.rs +++ b/taptrade-cli-demo/trader/src/communication/mod.rs @@ -9,22 +9,23 @@ use api::{OfferCreationResponse, OrderRequest}; impl OfferCreationResponse { fn _format_request(trader_setup: &TraderSettings) -> OrderRequest { let amount: u64; - let trade_type = match &trader_setup.trade_type { + let is_buy_order = match &trader_setup.trade_type { OfferType::Buy(val) => { amount = *val; - "buy" + true } OfferType::Sell(val) => { amount = *val; - "sell" + false } }; OrderRequest { - robohash_base91: trader_setup.robosats_robohash_base91.clone(), + robohash_hex: trader_setup.robosats_robohash_hex.clone(), amount_satoshi: amount, - order_type: trade_type.to_string(), + is_buy_order, bond_ratio: trader_setup.bond_ratio, + offer_duration_ts: trader_setup.duration_unix_ts, } } diff --git a/taptrade-cli-demo/trader/src/trading/maker_utils.rs b/taptrade-cli-demo/trader/src/trading/maker_utils.rs index 8a175f7..618f03d 100644 --- a/taptrade-cli-demo/trader/src/trading/maker_utils.rs +++ b/taptrade-cli-demo/trader/src/trading/maker_utils.rs @@ -7,13 +7,6 @@ use crate::wallet::{ }; use anyhow::Result; use bdk::database::MemoryDatabase; -// use bdk::{ -// bitcoin::block, -// blockchain::{Blockchain, ElectrumBlockchain}, -// electrum_client::Client, -// wallet::AddressIndex::LastUnused, -// Wallet, -// }; pub struct ActiveOffer {} @@ -22,20 +15,17 @@ impl ActiveOffer { trading_wallet: &TradingWallet, maker_config: &TraderSettings, ) -> Result { - let offer_conditions = OfferCreationResponse::fetch(maker_config)?; let trading_wallet = &trading_wallet.wallet; + // let offer_conditions = OfferCreationResponse::fetch(maker_config)?; let offer_conditions = OfferCreationResponse { // hardcoded for testing, locking_address is owned by .env xprv - locking_amount: 90000, + locking_amount_sat: 90000, bond_address: "tb1pfdvgfzwp8vhmelpv8w9kezz7nsmxw68jz6yehgze6mzx0t6r9t2qv9ynmm" .to_string(), }; - let bond = Bond::assemble(trading_wallet, &offer_conditions, maker_config)?; - let payout_pubkey = trading_wallet.get_address(bdk::wallet::AddressIndex::LastUnused)?; - let musig_data = MuSigData::create(&maker_config.wallet_xprv, trading_wallet.secp_ctx())?; Ok(ActiveOffer {}) diff --git a/taptrade-cli-demo/trader/src/wallet/bond.rs b/taptrade-cli-demo/trader/src/wallet/bond.rs index a557e0d..5915209 100644 --- a/taptrade-cli-demo/trader/src/wallet/bond.rs +++ b/taptrade-cli-demo/trader/src/wallet/bond.rs @@ -44,7 +44,7 @@ impl Bond { )); builder - .add_recipient(address.script_pubkey(), bond_target.locking_amount) + .add_recipient(address.script_pubkey(), bond_target.locking_amount_sat) .do_not_spend_change() .fee_rate(FeeRate::from_sat_per_vb(201.0)); diff --git a/taptrade-cli-demo/trader/taker.env b/taptrade-cli-demo/trader/taker.env index 318761a..d15ed00 100644 --- a/taptrade-cli-demo/trader/taker.env +++ b/taptrade-cli-demo/trader/taker.env @@ -1,7 +1,8 @@ ELECTRUM_ENDPOINT="ssl://mempool.space:40002" # testnet 4 electrum server COORDINATOR_ENDPOINT="http://127.0.0.1:3000" -ROBOHASH_BASE91="n!DyWmzXG3"1"*$S(GHG4RB2gvM" # "base91 of hex of sha256 of robot22" +ROBOHASH_HEX="169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d" # sha256 of "robot22" TRADE_TYPE="sell" PAYOUT_ADDRESS="tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" BOND_RATIO=5 XPRV="tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32" # wallet xprv +OFFER_DURATION_HOURS=48