diff --git a/taptrade-cli-demo/trader/src/trading/mod.rs b/taptrade-cli-demo/trader/src/trading/mod.rs index 602d3b2..401c19a 100644 --- a/taptrade-cli-demo/trader/src/trading/mod.rs +++ b/taptrade-cli-demo/trader/src/trading/mod.rs @@ -61,7 +61,7 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { let mut available_offers = PublicOffers::fetch(taker_config)?; while available_offers.offers.is_none() { - println!("No offers available, fetching again in 10 sec."); + debug!("No offers available, fetching again in 10 sec."); thread::sleep(Duration::from_secs(10)); available_offers = PublicOffers::fetch(taker_config)?; } @@ -74,12 +74,12 @@ pub fn run_taker(taker_config: &TraderSettings) -> Result<()> { if accepted_offer.fiat_confirmation_cli_input(taker_config)? { // this represents the "confirm payment" / "confirm fiat recieved" button TradeObligationsSatisfied::submit(&accepted_offer.offer_id_hex, taker_config)?; - println!("Waiting for other party to confirm the trade."); + debug!("Waiting for other party to confirm the trade."); // pull for other parties confirmation, then receive the transaction to create MuSig signature for (keyspend) to payout address let payout_keyspend_psbt = IsOfferReadyRequest::poll_payout(taker_config, &accepted_offer)?; // here we need to handle if the other party is not cooperating } else { - println!("Trade failed."); + error!("Trade failed."); panic!("Escrow to be implemented!"); } Ok(()) diff --git a/taptrade-cli-demo/trader/src/wallet/bond.rs b/taptrade-cli-demo/trader/src/wallet/bond.rs index bee743c..05bbae8 100644 --- a/taptrade-cli-demo/trader/src/wallet/bond.rs +++ b/taptrade-cli-demo/trader/src/wallet/bond.rs @@ -57,3 +57,156 @@ impl Bond { Ok(psbt) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::*; + use bdk::{ + bitcoin::{self, bip32::ExtendedPrivKey, psbt::PartiallySignedTransaction, Network}, + blockchain::ElectrumBlockchain, + database::MemoryDatabase, + electrum_client::Client, + keys::DescriptorPublicKey, + miniscript::Descriptor, + template::{Bip86, DescriptorTemplate}, + wallet::AddressInfo, + KeychainKind, SignOptions, SyncOptions, Wallet, + }; + use std::str::FromStr; + + fn setup_wallet(xprv_str: &str) -> Wallet { + let backend = ElectrumBlockchain::from(Client::new("ssl://mempool.space:40002").unwrap()); + let wallet_xprv = ExtendedPrivKey::from_str(xprv_str).unwrap(); + let wallet = Wallet::new( + Bip86(wallet_xprv, KeychainKind::External), + Some(Bip86(wallet_xprv, KeychainKind::Internal)), + Network::Testnet, + MemoryDatabase::default(), + ) + .unwrap(); + wallet.sync(&backend, SyncOptions::default()).unwrap(); + wallet + } + + #[test] + fn test_assemble_success() { + let wallet = setup_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"); + let bond_target = BondRequirementResponse { + bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), + locking_amount_sat: 10000, + }; + let trader_input = TraderSettings { + electrum_endpoint: "ssl://mempool.space:40002".to_string(), + coordinator_endpoint: "http://127.0.0.1:9999".to_string(), + robosats_robohash_hex: + "169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d".to_string(), + trade_type: OfferType::Buy(10000), + payout_address: "tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" + .to_string(), + bond_ratio: 12, + wallet_xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").unwrap(), + duration_unix_ts: 1783593911, // until when the order should stay available + }; + + let result = Bond::assemble(&wallet, &bond_target, &trader_input); + assert!(result.is_ok()); + } + + #[test] + fn test_assemble_invalid_address() { + let wallet = setup_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"); + let bond_target = BondRequirementResponse { + bond_address: "invalid_address".to_string(), + locking_amount_sat: 10000, + }; + let trader_input = TraderSettings { + electrum_endpoint: "ssl://mempool.space:40002".to_string(), + coordinator_endpoint: "http://127.0.0.1:9999".to_string(), + robosats_robohash_hex: + "169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d".to_string(), + trade_type: OfferType::Buy(10000), + payout_address: "tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" + .to_string(), + bond_ratio: 12, + wallet_xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").unwrap(), + duration_unix_ts: 1783593911, // until when the order should stay available + }; + + let result = Bond::assemble(&wallet, &bond_target, &trader_input); + assert!(result.is_err()); + } + + #[test] + fn test_assemble_mainnet_address() { + let wallet = setup_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"); + let bond_target = BondRequirementResponse { + bond_address: "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297" + .to_string(), + locking_amount_sat: 10000, + }; + let trader_input = TraderSettings { + electrum_endpoint: "ssl://mempool.space:40002".to_string(), + coordinator_endpoint: "http://127.0.0.1:9999".to_string(), + robosats_robohash_hex: + "169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d".to_string(), + trade_type: OfferType::Buy(10000), + payout_address: "tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" + .to_string(), + bond_ratio: 12, + wallet_xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").unwrap(), + duration_unix_ts: 1783593911, // until when the order should stay available + }; + + let result = Bond::assemble(&wallet, &bond_target, &trader_input); + assert!(result.is_err()); + } + + #[test] + fn test_assemble_insufficient_funds() { + let wallet = setup_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"); + let bond_target = BondRequirementResponse { + bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), + locking_amount_sat: 10000000000, // Very high amount + }; + let trader_input = TraderSettings { + electrum_endpoint: "ssl://mempool.space:40002".to_string(), + coordinator_endpoint: "http://127.0.0.1:9999".to_string(), + robosats_robohash_hex: + "169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d".to_string(), + trade_type: OfferType::Buy(10000), + payout_address: "tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" + .to_string(), + bond_ratio: 12, + wallet_xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").unwrap(), + duration_unix_ts: 1783593911, // until when the order should stay available + }; + + let result = Bond::assemble(&wallet, &bond_target, &trader_input); + assert!(result.is_err()); + } + + #[test] + fn test_assemble_zero_amount() { + let wallet = setup_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32"); + let bond_target = BondRequirementResponse { + bond_address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx".to_string(), + locking_amount_sat: 0, + }; + let trader_input = TraderSettings { + electrum_endpoint: "ssl://mempool.space:40002".to_string(), + coordinator_endpoint: "http://127.0.0.1:9999".to_string(), + robosats_robohash_hex: + "169b6049cf865eba7d01e1ad26975f1d5ff29d570297ff18d40a53c8281dff5d".to_string(), + trade_type: OfferType::Buy(10000), + payout_address: "tb1p37qg73t5y0l4un3q5dknzl8fgfhemghaap67wns45pzgrw2tasrq6kesxm" + .to_string(), + bond_ratio: 12, + wallet_xprv: ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").unwrap(), + duration_unix_ts: 1783593911, // until when the order should stay available + }; + + let result = Bond::assemble(&wallet, &bond_target, &trader_input); + assert!(result.is_err()); + } +}