diff --git a/swap/src/bin/swap_cli.rs b/swap/src/bin/swap_cli.rs index c59bcfe8..629d1061 100644 --- a/swap/src/bin/swap_cli.rs +++ b/swap/src/bin/swap_cli.rs @@ -35,7 +35,6 @@ use swap::{ protocol::{ bob, bob::{cancel::CancelError, Builder}, - SwapAmounts, }, seed::Seed, trace::init_tracing, @@ -89,21 +88,15 @@ async fn main() -> Result<()> { alice_peer_id, alice_addr, send_bitcoin, - receive_monero, } => { - let swap_amounts = SwapAmounts { - btc: send_bitcoin, - xmr: receive_monero, - }; - let (bitcoin_wallet, monero_wallet) = init_wallets(config, bitcoin_network, monero_network).await?; let swap_id = Uuid::new_v4(); info!( - "Swap sending {} and receiving {} started with ID {}", - send_bitcoin, receive_monero, swap_id + "Swap buy XMR with {} started with ID {}", + send_bitcoin, swap_id ); let bob_factory = Builder::new( @@ -116,7 +109,7 @@ async fn main() -> Result<()> { alice_peer_id, execution_params, ); - let (swap, event_loop) = bob_factory.with_init_params(swap_amounts).build().await?; + let (swap, event_loop) = bob_factory.with_init_params(send_bitcoin).build().await?; tokio::spawn(async move { event_loop.run().await }); bob::run(swap).await?; diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 1cf2f4a6..1b4c8f45 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -1,4 +1,4 @@ -use crate::{bitcoin, monero}; +use crate::bitcoin; use libp2p::{core::Multiaddr, PeerId}; use std::path::PathBuf; use uuid::Uuid; @@ -28,9 +28,6 @@ pub enum Command { #[structopt(long = "send-btc", help = "Bitcoin amount as floating point nr without denomination (e.g. 1.25)", parse(try_from_str = parse_btc))] send_bitcoin: bitcoin::Amount, - - #[structopt(long = "receive-xmr", help = "Monero amount as floating point nr without denomination (e.g. 125.1)", parse(try_from_str = parse_xmr))] - receive_monero: monero::Amount, }, History, Resume(Resume), @@ -92,8 +89,3 @@ fn parse_btc(str: &str) -> anyhow::Result { let amount = bitcoin::Amount::from_str_in(str, ::bitcoin::Denomination::Bitcoin)?; Ok(amount) } - -fn parse_xmr(str: &str) -> anyhow::Result { - let amount = monero::Amount::parse_monero(str)?; - Ok(amount) -} diff --git a/swap/src/database/bob.rs b/swap/src/database/bob.rs index 89a66436..260f6d84 100644 --- a/swap/src/database/bob.rs +++ b/swap/src/database/bob.rs @@ -1,6 +1,6 @@ use crate::{ monero::TransferProof, - protocol::{bob, bob::BobState, SwapAmounts}, + protocol::{bob, bob::BobState}, }; use ::bitcoin::hashes::core::fmt::Display; use monero_harness::rpc::wallet::BlockHeight; @@ -9,10 +9,10 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum Bob { Started { - state0: bob::State0, - amounts: SwapAmounts, + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + btc_amount: bitcoin::Amount, }, - Negotiated { + ExecutionSetupDone { state2: bob::State2, }, BtcLocked { @@ -46,8 +46,8 @@ pub enum BobEndState { impl From for Bob { fn from(bob_state: BobState) -> Self { match bob_state { - BobState::Started { state0, amounts } => Bob::Started { state0, amounts }, - BobState::Negotiated(state2) => Bob::Negotiated { state2 }, + BobState::Started { btc_amount } => Bob::Started { btc_amount }, + BobState::ExecutionSetupDone(state2) => Bob::ExecutionSetupDone { state2 }, BobState::BtcLocked(state3) => Bob::BtcLocked { state3 }, BobState::XmrLockProofReceived { state, @@ -78,8 +78,8 @@ impl From for Bob { impl From for BobState { fn from(db_state: Bob) -> Self { match db_state { - Bob::Started { state0, amounts } => BobState::Started { state0, amounts }, - Bob::Negotiated { state2 } => BobState::Negotiated(state2), + Bob::Started { btc_amount } => BobState::Started { btc_amount }, + Bob::ExecutionSetupDone { state2 } => BobState::ExecutionSetupDone(state2), Bob::BtcLocked { state3 } => BobState::BtcLocked(state3), Bob::XmrLockProofReceived { state, @@ -109,7 +109,7 @@ impl Display for Bob { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Bob::Started { .. } => write!(f, "Started"), - Bob::Negotiated { .. } => f.write_str("Negotiated"), + Bob::ExecutionSetupDone { .. } => f.write_str("Execution setup done"), Bob::BtcLocked { .. } => f.write_str("Bitcoin locked"), Bob::XmrLockProofReceived { .. } => { f.write_str("XMR lock transaction transfer proof received") diff --git a/swap/src/protocol/alice/event_loop.rs b/swap/src/protocol/alice/event_loop.rs index e887b170..1a84d38a 100644 --- a/swap/src/protocol/alice/event_loop.rs +++ b/swap/src/protocol/alice/event_loop.rs @@ -25,7 +25,7 @@ use tracing::{debug, error, trace, warn}; use uuid::Uuid; // TODO: Use dynamic -const RATE: u32 = 100; +pub const RATE: u32 = 100; #[allow(missing_debug_implementations)] pub struct MpscChannels { diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index eed474e1..7117365b 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -9,12 +9,11 @@ use crate::{ peer_tracker::{self, PeerTracker}, transport::build, }, - protocol::{alice, alice::TransferProof, bob, SwapAmounts}, + protocol::{alice, alice::TransferProof, bob}, seed::Seed, }; use anyhow::{bail, Error, Result}; use libp2p::{core::Multiaddr, identity::Keypair, NetworkBehaviour, PeerId}; -use rand::rngs::OsRng; use std::sync::Arc; use tracing::{debug, info}; use uuid::Uuid; @@ -69,7 +68,7 @@ pub struct Builder { enum InitParams { None, - New { swap_amounts: SwapAmounts }, + New { btc_amount: bitcoin::Amount }, } impl Builder { @@ -101,19 +100,17 @@ impl Builder { } } - pub fn with_init_params(self, swap_amounts: SwapAmounts) -> Self { + pub fn with_init_params(self, btc_amount: bitcoin::Amount) -> Self { Self { - init_params: InitParams::New { swap_amounts }, + init_params: InitParams::New { btc_amount }, ..self } } pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> { match self.init_params { - InitParams::New { swap_amounts } => { - let initial_state = self - .make_initial_state(swap_amounts.btc, swap_amounts.xmr, self.execution_params) - .await?; + InitParams::New { btc_amount } => { + let initial_state = BobState::Started { btc_amount }; let (event_loop, event_loop_handle) = self.init_event_loop()?; @@ -175,31 +172,6 @@ impl Builder { self.bitcoin_wallet.clone(), ) } - - async fn make_initial_state( - &self, - btc_to_swap: bitcoin::Amount, - xmr_to_swap: monero::Amount, - execution_params: ExecutionParams, - ) -> Result { - let amounts = SwapAmounts { - btc: btc_to_swap, - xmr: xmr_to_swap, - }; - - let refund_address = self.bitcoin_wallet.new_address().await?; - let state0 = bob::State0::new( - &mut OsRng, - btc_to_swap, - xmr_to_swap, - execution_params.bitcoin_cancel_timelock, - execution_params.bitcoin_punish_timelock, - refund_address, - execution_params.monero_finality_confirmations, - ); - - Ok(BobState::Started { state0, amounts }) - } } #[derive(Debug)] diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 4608bf86..17fafa2f 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -11,7 +11,6 @@ use crate::{ protocol::{ alice::{Message1, Message3}, bob::{EncryptedSignature, Message0, Message2, Message4}, - SwapAmounts, }, }; use anyhow::{anyhow, Result}; @@ -25,10 +24,9 @@ use std::fmt; #[derive(Debug, Clone)] pub enum BobState { Started { - state0: State0, - amounts: SwapAmounts, + btc_amount: bitcoin::Amount, }, - Negotiated(State2), + ExecutionSetupDone(State2), BtcLocked(State3), XmrLockProofReceived { state: State3, @@ -53,8 +51,8 @@ pub enum BobState { impl fmt::Display for BobState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - BobState::Started { .. } => write!(f, "started"), - BobState::Negotiated(..) => write!(f, "negotiated"), + BobState::Started { .. } => write!(f, "quote has been requested"), + BobState::ExecutionSetupDone(..) => write!(f, "execution setup done"), BobState::BtcLocked(..) => write!(f, "btc is locked"), BobState::XmrLockProofReceived { .. } => { write!(f, "XMR lock transaction transfer proof received") diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 7aec3404..d645e83e 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -4,13 +4,11 @@ use crate::{ database::{Database, Swap}, execution_params::ExecutionParams, monero, - protocol::{ - bob::{self, event_loop::EventLoopHandle, state::*, QuoteRequest}, - SwapAmounts, - }, + protocol::bob::{self, event_loop::EventLoopHandle, state::*, QuoteRequest}, }; use anyhow::{bail, Result}; use async_recursion::async_recursion; +use rand::rngs::OsRng; use std::sync::Arc; use tokio::select; use tracing::info; @@ -67,12 +65,20 @@ async fn run_until_internal( Ok(state) } else { match state { - BobState::Started { state0, amounts } => { + BobState::Started { btc_amount } => { + let bitcoin_refund_address = bitcoin_wallet.new_address().await?; + event_loop_handle.dial().await?; - let state2 = negotiate(state0, amounts, &mut event_loop_handle).await?; + let state2 = request_quote_and_setup( + btc_amount, + &mut event_loop_handle, + execution_params, + bitcoin_refund_address, + ) + .await?; - let state = BobState::Negotiated(state2); + let state = BobState::ExecutionSetupDone(state2); let db_state = state.clone().into(); db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?; run_until_internal( @@ -87,7 +93,7 @@ async fn run_until_internal( ) .await } - BobState::Negotiated(state2) => { + BobState::ExecutionSetupDone(state2) => { // Do not lock Bitcoin if not connected to Alice. event_loop_handle.dial().await?; // Alice and Bob have exchanged info @@ -368,21 +374,27 @@ async fn run_until_internal( } } -pub async fn negotiate( - state0: crate::protocol::bob::state::State0, - amounts: SwapAmounts, +pub async fn request_quote_and_setup( + btc_amount: bitcoin::Amount, event_loop_handle: &mut EventLoopHandle, + execution_params: ExecutionParams, + bitcoin_refund_address: bitcoin::Address, ) -> Result { - tracing::trace!("Starting negotiate"); event_loop_handle - .send_quote_request(QuoteRequest { - btc_amount: amounts.btc, - }) + .send_quote_request(QuoteRequest { btc_amount }) .await?; - // TODO: Use this once Bob's CLI is modified to only pass xmr amount in - // argument. - let _quote_response = event_loop_handle.recv_quote_response().await?; + let quote_response = event_loop_handle.recv_quote_response().await?; + + let state0 = State0::new( + &mut OsRng, + btc_amount, + quote_response.xmr_amount, + execution_params.bitcoin_cancel_timelock, + execution_params.bitcoin_punish_timelock, + bitcoin_refund_address, + execution_params.monero_finality_confirmations, + ); let state2 = event_loop_handle.execution_setup(state0).await?; diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index ea0384f4..1b6edd42 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -12,7 +12,12 @@ use swap::{ execution_params, execution_params::{ExecutionParams, GetExecutionParams}, monero, - protocol::{alice, alice::AliceState, bob, bob::BobState, SwapAmounts}, + protocol::{ + alice, + alice::{event_loop::RATE, AliceState}, + bob, + bob::BobState, + }, seed::Seed, }; use tempfile::tempdir; @@ -66,7 +71,8 @@ impl BobEventLoopJoinHandle { pub struct AliceEventLoopJoinHandle(JoinHandle<()>); pub struct TestContext { - swap_amounts: SwapAmounts, + btc_amount: bitcoin::Amount, + xmr_amount: monero::Amount, alice_starting_balances: StartingBalances, alice_bitcoin_wallet: Arc, @@ -84,7 +90,7 @@ impl TestContext { let (swap, event_loop) = self .bob_params .builder() - .with_init_params(self.swap_amounts) + .with_init_params(self.btc_amount) .build() .await .unwrap(); @@ -116,7 +122,7 @@ impl TestContext { let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( btc_balance_after_swap, - self.alice_starting_balances.btc + self.swap_amounts.btc + self.alice_starting_balances.btc + self.btc_amount - bitcoin::Amount::from_sat(bitcoin::TX_FEE) ); @@ -126,7 +132,7 @@ impl TestContext { .get_balance() .await .unwrap(); - assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr); + assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.xmr_amount); } pub async fn assert_alice_refunded(&mut self) { @@ -155,7 +161,7 @@ impl TestContext { .get_balance() .await .unwrap(); - assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); + assert_eq!(xmr_balance_after_swap, self.xmr_amount); } pub async fn assert_alice_punished(&self, state: AliceState) { @@ -164,7 +170,7 @@ impl TestContext { let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( btc_balance_after_swap, - self.alice_starting_balances.btc + self.swap_amounts.btc + self.alice_starting_balances.btc + self.btc_amount - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) ); @@ -174,7 +180,7 @@ impl TestContext { .get_balance() .await .unwrap(); - assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr); + assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.xmr_amount); } pub async fn assert_bob_redeemed(&self, state: BobState) { @@ -193,7 +199,7 @@ impl TestContext { let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( btc_balance_after_swap, - self.bob_starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee + self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee ); // Ensure that Bob's balance is refreshed as we use a newly created wallet @@ -206,7 +212,7 @@ impl TestContext { let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap(); assert_eq!( xmr_balance_after_swap, - self.bob_starting_balances.xmr + self.swap_amounts.xmr + self.bob_starting_balances.xmr + self.xmr_amount ); } @@ -258,7 +264,7 @@ impl TestContext { let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap(); assert_eq!( btc_balance_after_swap, - self.bob_starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee + self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee ); let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap(); @@ -280,13 +286,11 @@ where let (monero, containers) = testutils::init_containers(&cli).await; - let swap_amounts = SwapAmounts { - btc: bitcoin::Amount::from_sat(1_000_000), - xmr: monero::Amount::from_piconero(1_000_000_000_000), - }; + let btc_amount = bitcoin::Amount::from_sat(1_000_000); + let xmr_amount = monero::Amount::from_monero(btc_amount.as_btc() * RATE as f64).unwrap(); let alice_starting_balances = StartingBalances { - xmr: swap_amounts.xmr * 10, + xmr: xmr_amount * 10, btc: bitcoin::Amount::ZERO, }; @@ -311,7 +315,7 @@ where let bob_starting_balances = StartingBalances { xmr: monero::Amount::ZERO, - btc: swap_amounts.btc * 10, + btc: btc_amount * 10, }; let (bob_bitcoin_wallet, bob_monero_wallet) = init_test_wallets( @@ -350,7 +354,8 @@ where }; let test = TestContext { - swap_amounts, + btc_amount, + xmr_amount, alice_starting_balances, alice_bitcoin_wallet, alice_monero_wallet,