diff --git a/swap/src/bitcoin.rs b/swap/src/bitcoin.rs index 3511e395..cdd3a2bb 100644 --- a/swap/src/bitcoin.rs +++ b/swap/src/bitcoin.rs @@ -231,11 +231,26 @@ pub trait GetRawTransaction { async fn get_raw_transaction(&self, txid: Txid) -> Result; } +#[async_trait] +pub trait NewAddress { + async fn new_address(&self) -> Result
; +} + #[async_trait] pub trait GetNetwork { fn get_network(&self) -> Network; } +#[async_trait] +pub trait Balance { + async fn balance(&self) -> Result; +} + +#[async_trait] +pub trait FetchTransactionFee { + async fn transaction_fee(&self, txid: Txid) -> Result; +} + pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result { let adaptor = Adaptor::>::default(); diff --git a/swap/src/bitcoin/wallet.rs b/swap/src/bitcoin/wallet.rs index cecfd62e..00d55115 100644 --- a/swap/src/bitcoin/wallet.rs +++ b/swap/src/bitcoin/wallet.rs @@ -1,8 +1,9 @@ use crate::{ bitcoin::{ - timelocks::BlockHeight, Address, Amount, BroadcastSignedTransaction, BuildTxLockPsbt, - GetBlockHeight, GetNetwork, GetRawTransaction, SignTxLock, Transaction, - TransactionBlockHeight, TxLock, WaitForTransactionFinality, WatchForRawTransaction, + timelocks::BlockHeight, Address, Amount, Balance, BroadcastSignedTransaction, + BuildTxLockPsbt, FetchTransactionFee, GetBlockHeight, GetNetwork, GetRawTransaction, + NewAddress, SignTxLock, Transaction, TransactionBlockHeight, TxLock, + WaitForTransactionFinality, WatchForRawTransaction, }, config::Config, }; @@ -30,33 +31,6 @@ impl Wallet { network, }) } - - pub async fn balance(&self) -> Result { - let balance = self.inner.balance().await?; - Ok(balance) - } - - pub async fn new_address(&self) -> Result
{ - self.inner.new_address().await.map_err(Into::into) - } - - pub async fn transaction_fee(&self, txid: Txid) -> Result { - let fee = self - .inner - .get_wallet_transaction(txid) - .await - .map(|res| { - res.fee.map(|signed_amount| { - signed_amount - .abs() - .to_unsigned() - .expect("Absolute value is always positive") - }) - })? - .context("Rpc response did not contain a fee")?; - - Ok(fee) - } } #[async_trait] @@ -191,8 +165,44 @@ impl WaitForTransactionFinality for Wallet { } } +#[async_trait] +impl NewAddress for Wallet { + async fn new_address(&self) -> Result
{ + self.inner.new_address().await.map_err(Into::into) + } +} + impl GetNetwork for Wallet { fn get_network(&self) -> bitcoin::Network { self.network } } + +#[async_trait] +impl Balance for Wallet { + async fn balance(&self) -> Result { + let balance = self.inner.balance().await?; + Ok(balance) + } +} + +#[async_trait] +impl FetchTransactionFee for Wallet { + async fn transaction_fee(&self, txid: Txid) -> Result { + let fee = self + .inner + .get_wallet_transaction(txid) + .await + .map(|res| { + res.fee.map(|signed_amount| { + signed_amount + .abs() + .to_unsigned() + .expect("Absolute value is always positive") + }) + })? + .context("Rpc response did not contain a fee")?; + + Ok(fee) + } +} diff --git a/swap/src/main.rs b/swap/src/main.rs index e5c23fad..63c42801 100644 --- a/swap/src/main.rs +++ b/swap/src/main.rs @@ -12,7 +12,11 @@ #![forbid(unsafe_code)] #![allow(non_snake_case)] -use crate::cli::{Command, Options, Resume}; +use crate::{ + bitcoin::Balance as _, + cli::{Command, Options, Resume}, + monero::Balance as _, +}; use anyhow::{Context, Result}; use config::Config; use database::Database; @@ -242,7 +246,7 @@ async fn setup_wallets( ); let monero_wallet = monero::Wallet::new(monero_wallet_rpc_url, config.monero_network); - let monero_balance = monero_wallet.get_balance().await?; + let monero_balance = monero_wallet.balance().await?; info!( "Connection to Monero wallet succeeded, balance: {}", monero_balance diff --git a/swap/src/monero.rs b/swap/src/monero.rs index caec8864..0dc5afca 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -8,6 +8,7 @@ use crate::bitcoin; use ::bitcoin::hashes::core::fmt::Formatter; use anyhow::Result; use async_trait::async_trait; +use monero_harness::rpc::wallet::BlockHeight; use rand::{CryptoRng, RngCore}; use rust_decimal::{ prelude::{FromPrimitive, ToPrimitive}, @@ -207,6 +208,22 @@ pub trait CreateWalletForOutput { ) -> anyhow::Result<()>; } +#[async_trait] +pub trait Balance { + /// Get the balance of the primary account. + async fn balance(&self) -> Result; +} + +#[async_trait] +pub trait Refresh { + async fn refresh(&self) -> Result<()>; +} + +#[async_trait] +pub trait FetchBlockHeight { + async fn block_height(&self) -> Result; +} + #[derive(thiserror::Error, Debug, Clone, PartialEq)] #[error("Overflow, cannot convert {0} to u64")] pub struct OverflowError(pub String); diff --git a/swap/src/monero/wallet.rs b/swap/src/monero/wallet.rs index 2f26568d..a0ade222 100644 --- a/swap/src/monero/wallet.rs +++ b/swap/src/monero/wallet.rs @@ -1,13 +1,13 @@ use crate::monero::{ - Amount, CreateWalletForOutput, InsufficientFunds, PrivateViewKey, PublicViewKey, Transfer, - TransferProof, TxHash, WatchForTransfer, + Amount, Balance, CreateWalletForOutput, FetchBlockHeight, InsufficientFunds, PrivateViewKey, + PublicViewKey, Refresh, Transfer, TransferProof, TxHash, WatchForTransfer, }; use ::monero::{Address, Network, PrivateKey, PublicKey}; use anyhow::Result; use async_trait::async_trait; use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _}; use bitcoin::hashes::core::sync::atomic::AtomicU32; -use monero_harness::rpc::wallet; +use monero_harness::rpc::{wallet, wallet::BlockHeight}; use std::{ str::FromStr, sync::{atomic::Ordering, Arc}, @@ -29,13 +29,6 @@ impl Wallet { network, } } - - /// Get the balance of the primary account. - pub async fn get_balance(&self) -> Result { - let amount = self.inner.get_balance(0).await?; - - Ok(Amount::from_piconero(amount)) - } } #[async_trait] @@ -166,3 +159,27 @@ impl WatchForTransfer for Wallet { Ok(()) } } + +#[async_trait] +impl Balance for Wallet { + async fn balance(&self) -> Result { + let amount = self.inner.get_balance(0).await?; + + Ok(Amount::from_piconero(amount)) + } +} + +#[async_trait] +impl Refresh for Wallet { + async fn refresh(&self) -> Result<()> { + self.inner.refresh().await?; + Ok(()) + } +} + +#[async_trait] +impl FetchBlockHeight for Wallet { + async fn block_height(&self) -> Result { + self.inner.block_height().await + } +} diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 22af28df..609be192 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -44,17 +44,27 @@ pub mod swap; mod swap_response; mod transfer_proof; -pub struct Swap { +pub trait BitcoinWallet: swap::BitcoinWallet + bitcoin::NewAddress {} +impl BitcoinWallet for bitcoin::Wallet {} + +pub trait MoneroWallet: monero::Transfer + monero::CreateWalletForOutput + Send + Sync {} +impl MoneroWallet for monero::Wallet {} + +pub struct Swap { pub state: AliceState, pub event_loop_handle: EventLoopHandle, - pub bitcoin_wallet: Arc, - pub monero_wallet: Arc, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, pub config: Config, pub swap_id: Uuid, pub db: Database, } -pub struct Builder { +pub struct Builder +where + B: BitcoinWallet + bitcoin::NewAddress, + M: MoneroWallet, +{ swap_id: Uuid, identity: Keypair, peer_id: PeerId, @@ -63,8 +73,8 @@ pub struct Builder { listen_address: Multiaddr, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, init_params: InitParams, } @@ -74,13 +84,17 @@ enum InitParams { New { swap_amounts: SwapAmounts }, } -impl Builder { +impl Builder +where + B: BitcoinWallet + bitcoin::NewAddress, + M: MoneroWallet, +{ pub async fn new( seed: Seed, config: Config, swap_id: Uuid, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, db_path: PathBuf, listen_address: Multiaddr, ) -> Self { @@ -108,7 +122,7 @@ impl Builder { } } - pub async fn build(self) -> Result<(Swap, EventLoop)> { + pub async fn build(self) -> Result<(Swap, EventLoop)> { match self.init_params { InitParams::New { swap_amounts } => { let initial_state = self diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index 2abe8e67..9c944580 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -2,15 +2,10 @@ //! Alice holds XMR and wishes receive BTC. use crate::{ bitcoin, - bitcoin::{ - timelocks::ExpiredTimelocks, TransactionBlockHeight, WaitForTransactionFinality, - WatchForRawTransaction, - }, + bitcoin::timelocks::ExpiredTimelocks, config::Config, database, database::Database, - monero, - monero::CreateWalletForOutput, protocol::{ alice, alice::{ @@ -22,7 +17,7 @@ use crate::{ publish_cancel_transaction, wait_for_bitcoin_encrypted_signature, wait_for_bitcoin_refund, wait_for_locked_bitcoin, }, - AliceState, + AliceState, MoneroWallet, }, }, }; @@ -41,6 +36,20 @@ trait Rng: RngCore + CryptoRng + Send {} impl Rng for T where T: RngCore + CryptoRng + Send {} +pub trait BitcoinWallet: + bitcoin::WatchForRawTransaction + + bitcoin::WaitForTransactionFinality + + bitcoin::TransactionBlockHeight + + bitcoin::GetBlockHeight + + bitcoin::BroadcastSignedTransaction + + bitcoin::GetRawTransaction + + Send + + Sync +{ +} + +impl BitcoinWallet for bitcoin::Wallet {} + pub fn is_complete(state: &AliceState) -> bool { matches!( state, @@ -51,14 +60,22 @@ pub fn is_complete(state: &AliceState) -> bool { ) } -pub async fn run(swap: alice::Swap) -> Result { +pub async fn run(swap: alice::Swap) -> Result +where + B: BitcoinWallet, + M: MoneroWallet, +{ run_until(swap, is_complete).await } -pub async fn run_until( - swap: alice::Swap, +pub async fn run_until( + swap: alice::Swap, is_target_state: fn(&AliceState) -> bool, -) -> Result { +) -> Result +where + B: BitcoinWallet, + M: MoneroWallet, +{ run_until_internal( swap.state, is_target_state, @@ -75,16 +92,20 @@ pub async fn run_until( // State machine driver for swap execution #[async_recursion] #[allow(clippy::too_many_arguments)] -async fn run_until_internal( +async fn run_until_internal( state: AliceState, is_target_state: fn(&AliceState) -> bool, mut event_loop_handle: EventLoopHandle, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, config: Config, swap_id: Uuid, db: Database, -) -> Result { +) -> Result +where + B: BitcoinWallet, + M: MoneroWallet, +{ info!("Current state:{}", state); if is_target_state(&state) { Ok(state) diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index f4ca2e52..a53d5c8f 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -42,17 +42,20 @@ pub mod swap; mod swap_request; mod transfer_proof; -pub struct Swap { +pub struct Swap { pub state: BobState, pub event_loop_handle: bob::EventLoopHandle, pub db: Database, - pub bitcoin_wallet: Arc, - pub monero_wallet: Arc, + pub bitcoin_wallet: Arc, + pub monero_wallet: Arc, pub config: Config, pub swap_id: Uuid, } -pub struct Builder { +pub struct Builder +where + B: bitcoin::NewAddress, +{ swap_id: Uuid, identity: Keypair, peer_id: PeerId, @@ -61,8 +64,8 @@ pub struct Builder { alice_address: Multiaddr, alice_peer_id: PeerId, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, init_params: InitParams, config: Config, @@ -73,14 +76,17 @@ enum InitParams { New { swap_amounts: SwapAmounts }, } -impl Builder { +impl Builder +where + B: bitcoin::NewAddress, +{ #[allow(clippy::too_many_arguments)] pub fn new( seed: Seed, db_path: PathBuf, swap_id: Uuid, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, alice_address: Multiaddr, alice_peer_id: PeerId, config: Config, @@ -109,7 +115,7 @@ impl Builder { } } - pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> { + pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> { match self.init_params { InitParams::New { swap_amounts } => { let initial_state = self @@ -164,6 +170,7 @@ impl Builder { } } } + fn init_event_loop( &self, ) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> { diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 767372be..54926433 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -17,6 +17,28 @@ use tokio::select; use tracing::info; use uuid::Uuid; +pub trait BitcoinWallet: + bitcoin::BuildTxLockPsbt + + bitcoin::GetNetwork + + bitcoin::SignTxLock + + bitcoin::BroadcastSignedTransaction + + bitcoin::WatchForRawTransaction + + bitcoin::TransactionBlockHeight + + bitcoin::GetBlockHeight + + bitcoin::GetRawTransaction + + bitcoin::WaitForTransactionFinality + + Send + + Sync +{ +} +impl BitcoinWallet for bitcoin::Wallet {} + +pub trait MoneroWallet: + monero::WatchForTransfer + monero::FetchBlockHeight + monero::CreateWalletForOutput + Send + Sync +{ +} +impl MoneroWallet for monero::Wallet {} + pub fn is_complete(state: &BobState) -> bool { matches!( state, @@ -28,14 +50,22 @@ pub fn is_complete(state: &BobState) -> bool { } #[allow(clippy::too_many_arguments)] -pub async fn run(swap: bob::Swap) -> Result { +pub async fn run(swap: bob::Swap) -> Result +where + B: BitcoinWallet, + M: MoneroWallet, +{ run_until(swap, is_complete).await } -pub async fn run_until( - swap: bob::Swap, +pub async fn run_until( + swap: bob::Swap, is_target_state: fn(&BobState) -> bool, -) -> Result { +) -> Result +where + B: BitcoinWallet, + M: MoneroWallet, +{ run_until_internal( swap.state, is_target_state, @@ -53,19 +83,21 @@ pub async fn run_until( // State machine driver for swap execution #[allow(clippy::too_many_arguments)] #[async_recursion] -async fn run_until_internal( +async fn run_until_internal( state: BobState, is_target_state: fn(&BobState) -> bool, mut event_loop_handle: EventLoopHandle, db: Database, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, mut rng: R, swap_id: Uuid, config: Config, ) -> Result where R: RngCore + CryptoRng + Send, + B: BitcoinWallet, + M: MoneroWallet, { info!("Current state: {}", state); if is_target_state(&state) { @@ -139,8 +171,7 @@ where // TODO: This can be optimized further by extracting the block height when // tx-lock was included. However, scanning a few more blocks won't do any harm // and is simpler. - let monero_wallet_restore_blockheight = - monero_wallet.inner.block_height().await?; + let monero_wallet_restore_blockheight = monero_wallet.block_height().await?; select! { transfer_proof = transfer_proof_watcher => { @@ -387,15 +418,16 @@ where } } -pub async fn negotiate( +pub async fn negotiate( state0: crate::protocol::bob::state::State0, amounts: SwapAmounts, swarm: &mut EventLoopHandle, mut rng: R, - bitcoin_wallet: Arc, + bitcoin_wallet: Arc, ) -> Result where R: RngCore + CryptoRng + Send, + W: bitcoin::BuildTxLockPsbt + bitcoin::GetNetwork, { tracing::trace!("Starting negotiate"); swarm diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index e21aa1a4..aa701a9e 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -24,18 +24,26 @@ pub struct StartingBalances { pub btc: bitcoin::Amount, } -struct AliceParams { +struct AliceParams +where + B: swap::protocol::alice::BitcoinWallet, + M: swap::protocol::alice::MoneroWallet, +{ seed: Seed, config: Config, swap_id: Uuid, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, db_path: PathBuf, listen_address: Multiaddr, } -impl AliceParams { - pub async fn builder(&self) -> alice::Builder { +impl AliceParams +where + B: swap::protocol::alice::BitcoinWallet + bitcoin::NewAddress, + M: swap::protocol::alice::MoneroWallet, +{ + pub async fn builder(&self) -> alice::Builder { alice::Builder::new( self.seed, self.config, @@ -53,19 +61,22 @@ impl AliceParams { } } -struct BobParams { +struct BobParams { seed: Seed, db_path: PathBuf, swap_id: Uuid, - bitcoin_wallet: Arc, - monero_wallet: Arc, + bitcoin_wallet: Arc, + monero_wallet: Arc, alice_address: Multiaddr, alice_peer_id: PeerId, config: Config, } -impl BobParams { - pub fn builder(&self) -> bob::Builder { +impl BobParams +where + B: bitcoin::NewAddress, +{ + pub fn builder(&self) -> bob::Builder { bob::Builder::new( self.seed, self.db_path.clone(), @@ -79,22 +90,37 @@ impl BobParams { } } -pub struct TestContext { +pub struct TestContext +where + BA: swap::protocol::alice::BitcoinWallet, + MA: swap::protocol::alice::MoneroWallet, + BB: swap::protocol::bob::swap::BitcoinWallet, + MB: swap::protocol::bob::swap::MoneroWallet, +{ swap_amounts: SwapAmounts, - alice_params: AliceParams, + alice_params: AliceParams, alice_starting_balances: StartingBalances, - alice_bitcoin_wallet: Arc, - alice_monero_wallet: Arc, + alice_bitcoin_wallet: Arc, + alice_monero_wallet: Arc, - bob_params: BobParams, + bob_params: BobParams, bob_starting_balances: StartingBalances, - bob_bitcoin_wallet: Arc, - bob_monero_wallet: Arc, + bob_bitcoin_wallet: Arc, + bob_monero_wallet: Arc, } -impl TestContext { - pub async fn new_swap_as_alice(&mut self) -> alice::Swap { +impl TestContext +where + BA: swap::protocol::alice::BitcoinWallet + swap::bitcoin::Balance, + MA: swap::protocol::alice::MoneroWallet + swap::monero::Balance + swap::monero::Refresh, + BB: swap::protocol::bob::swap::BitcoinWallet + + swap::bitcoin::NewAddress + + swap::bitcoin::FetchTransactionFee + + swap::bitcoin::Balance, + MB: swap::protocol::bob::swap::MoneroWallet + swap::monero::Balance + swap::monero::Refresh, +{ + pub async fn new_swap_as_alice(&mut self) -> alice::Swap { let (swap, mut event_loop) = self .alice_params .builder() @@ -109,7 +135,7 @@ impl TestContext { swap } - pub async fn new_swap_as_bob(&mut self) -> bob::Swap { + pub async fn new_swap_as_bob(&mut self) -> bob::Swap { let (swap, event_loop) = self .bob_params .builder() @@ -123,7 +149,7 @@ impl TestContext { swap } - pub async fn recover_alice_from_db(&mut self) -> alice::Swap { + pub async fn recover_alice_from_db(&mut self) -> alice::Swap { let (swap, mut event_loop) = self.alice_params.builder().await.build().await.unwrap(); tokio::spawn(async move { event_loop.run().await }); @@ -131,7 +157,7 @@ impl TestContext { swap } - pub async fn recover_bob_from_db(&mut self) -> bob::Swap { + pub async fn recover_bob_from_db(&mut self) -> bob::Swap { let (swap, event_loop) = self.bob_params.builder().build().await.unwrap(); tokio::spawn(async move { event_loop.run().await }); @@ -149,12 +175,7 @@ impl TestContext { - bitcoin::Amount::from_sat(bitcoin::TX_FEE) ); - let xmr_balance_after_swap = self - .alice_monero_wallet - .as_ref() - .get_balance() - .await - .unwrap(); + let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap(); assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr); } @@ -165,18 +186,8 @@ impl TestContext { assert_eq!(btc_balance_after_swap, self.alice_starting_balances.btc); // Ensure that Alice's balance is refreshed as we use a newly created wallet - self.alice_monero_wallet - .as_ref() - .inner - .refresh() - .await - .unwrap(); - let xmr_balance_after_swap = self - .alice_monero_wallet - .as_ref() - .get_balance() - .await - .unwrap(); + self.alice_monero_wallet.as_ref().refresh().await.unwrap(); + let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap(); assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); } @@ -190,12 +201,7 @@ impl TestContext { - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) ); - let xmr_balance_after_swap = self - .alice_monero_wallet - .as_ref() - .get_balance() - .await - .unwrap(); + let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap(); assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr); } @@ -219,13 +225,8 @@ impl TestContext { ); // Ensure that Bob's balance is refreshed as we use a newly created wallet - self.bob_monero_wallet - .as_ref() - .inner - .refresh() - .await - .unwrap(); - let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap(); + self.bob_monero_wallet.as_ref().refresh().await.unwrap(); + let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().balance().await.unwrap(); assert_eq!( xmr_balance_after_swap, self.bob_starting_balances.xmr + self.swap_amounts.xmr @@ -260,7 +261,7 @@ impl TestContext { // Since we cannot be sure who submitted it we have to assert accordingly assert!(alice_submitted_cancel || bob_submitted_cancel); - let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap(); + let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().balance().await.unwrap(); assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr); } @@ -283,14 +284,14 @@ impl TestContext { self.bob_starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee ); - let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap(); + let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().balance().await.unwrap(); assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr); } } pub async fn setup_test(testfn: T) where - T: Fn(TestContext) -> F, + T: Fn(TestContext) -> F, F: Future, { let cli = Cli::default();