Replace hard wallet types with traits

This commit is contained in:
Franck Royer 2021-01-28 14:06:35 +11:00
parent 1c1c49071c
commit 921ba29256
No known key found for this signature in database
GPG key ID: A82ED75A8DFC50A4
10 changed files with 282 additions and 144 deletions

View file

@ -231,11 +231,26 @@ pub trait GetRawTransaction {
async fn get_raw_transaction(&self, txid: Txid) -> Result<Transaction>; async fn get_raw_transaction(&self, txid: Txid) -> Result<Transaction>;
} }
#[async_trait]
pub trait NewAddress {
async fn new_address(&self) -> Result<Address>;
}
#[async_trait] #[async_trait]
pub trait GetNetwork { pub trait GetNetwork {
fn get_network(&self) -> Network; fn get_network(&self) -> Network;
} }
#[async_trait]
pub trait Balance {
async fn balance(&self) -> Result<Amount>;
}
#[async_trait]
pub trait FetchTransactionFee {
async fn transaction_fee(&self, txid: Txid) -> Result<Amount>;
}
pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result<SecretKey> { pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result<SecretKey> {
let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default(); let adaptor = Adaptor::<Sha256, Deterministic<Sha256>>::default();

View file

@ -1,8 +1,9 @@
use crate::{ use crate::{
bitcoin::{ bitcoin::{
timelocks::BlockHeight, Address, Amount, BroadcastSignedTransaction, BuildTxLockPsbt, timelocks::BlockHeight, Address, Amount, Balance, BroadcastSignedTransaction,
GetBlockHeight, GetNetwork, GetRawTransaction, SignTxLock, Transaction, BuildTxLockPsbt, FetchTransactionFee, GetBlockHeight, GetNetwork, GetRawTransaction,
TransactionBlockHeight, TxLock, WaitForTransactionFinality, WatchForRawTransaction, NewAddress, SignTxLock, Transaction, TransactionBlockHeight, TxLock,
WaitForTransactionFinality, WatchForRawTransaction,
}, },
config::Config, config::Config,
}; };
@ -30,33 +31,6 @@ impl Wallet {
network, network,
}) })
} }
pub async fn balance(&self) -> Result<Amount> {
let balance = self.inner.balance().await?;
Ok(balance)
}
pub async fn new_address(&self) -> Result<Address> {
self.inner.new_address().await.map_err(Into::into)
}
pub async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
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] #[async_trait]
@ -191,8 +165,44 @@ impl WaitForTransactionFinality for Wallet {
} }
} }
#[async_trait]
impl NewAddress for Wallet {
async fn new_address(&self) -> Result<Address> {
self.inner.new_address().await.map_err(Into::into)
}
}
impl GetNetwork for Wallet { impl GetNetwork for Wallet {
fn get_network(&self) -> bitcoin::Network { fn get_network(&self) -> bitcoin::Network {
self.network self.network
} }
} }
#[async_trait]
impl Balance for Wallet {
async fn balance(&self) -> Result<Amount> {
let balance = self.inner.balance().await?;
Ok(balance)
}
}
#[async_trait]
impl FetchTransactionFee for Wallet {
async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
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)
}
}

View file

@ -12,7 +12,11 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![allow(non_snake_case)] #![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 anyhow::{Context, Result};
use config::Config; use config::Config;
use database::Database; 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_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!( info!(
"Connection to Monero wallet succeeded, balance: {}", "Connection to Monero wallet succeeded, balance: {}",
monero_balance monero_balance

View file

@ -8,6 +8,7 @@ use crate::bitcoin;
use ::bitcoin::hashes::core::fmt::Formatter; use ::bitcoin::hashes::core::fmt::Formatter;
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use monero_harness::rpc::wallet::BlockHeight;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use rust_decimal::{ use rust_decimal::{
prelude::{FromPrimitive, ToPrimitive}, prelude::{FromPrimitive, ToPrimitive},
@ -207,6 +208,22 @@ pub trait CreateWalletForOutput {
) -> anyhow::Result<()>; ) -> anyhow::Result<()>;
} }
#[async_trait]
pub trait Balance {
/// Get the balance of the primary account.
async fn balance(&self) -> Result<Amount>;
}
#[async_trait]
pub trait Refresh {
async fn refresh(&self) -> Result<()>;
}
#[async_trait]
pub trait FetchBlockHeight {
async fn block_height(&self) -> Result<BlockHeight>;
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)] #[derive(thiserror::Error, Debug, Clone, PartialEq)]
#[error("Overflow, cannot convert {0} to u64")] #[error("Overflow, cannot convert {0} to u64")]
pub struct OverflowError(pub String); pub struct OverflowError(pub String);

View file

@ -1,13 +1,13 @@
use crate::monero::{ use crate::monero::{
Amount, CreateWalletForOutput, InsufficientFunds, PrivateViewKey, PublicViewKey, Transfer, Amount, Balance, CreateWalletForOutput, FetchBlockHeight, InsufficientFunds, PrivateViewKey,
TransferProof, TxHash, WatchForTransfer, PublicViewKey, Refresh, Transfer, TransferProof, TxHash, WatchForTransfer,
}; };
use ::monero::{Address, Network, PrivateKey, PublicKey}; use ::monero::{Address, Network, PrivateKey, PublicKey};
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _}; use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
use bitcoin::hashes::core::sync::atomic::AtomicU32; use bitcoin::hashes::core::sync::atomic::AtomicU32;
use monero_harness::rpc::wallet; use monero_harness::rpc::{wallet, wallet::BlockHeight};
use std::{ use std::{
str::FromStr, str::FromStr,
sync::{atomic::Ordering, Arc}, sync::{atomic::Ordering, Arc},
@ -29,13 +29,6 @@ impl Wallet {
network, network,
} }
} }
/// Get the balance of the primary account.
pub async fn get_balance(&self) -> Result<Amount> {
let amount = self.inner.get_balance(0).await?;
Ok(Amount::from_piconero(amount))
}
} }
#[async_trait] #[async_trait]
@ -166,3 +159,27 @@ impl WatchForTransfer for Wallet {
Ok(()) Ok(())
} }
} }
#[async_trait]
impl Balance for Wallet {
async fn balance(&self) -> Result<Amount> {
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<BlockHeight> {
self.inner.block_height().await
}
}

View file

@ -44,17 +44,27 @@ pub mod swap;
mod swap_response; mod swap_response;
mod transfer_proof; 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<B, M> {
pub state: AliceState, pub state: AliceState,
pub event_loop_handle: EventLoopHandle, pub event_loop_handle: EventLoopHandle,
pub bitcoin_wallet: Arc<bitcoin::Wallet>, pub bitcoin_wallet: Arc<B>,
pub monero_wallet: Arc<monero::Wallet>, pub monero_wallet: Arc<M>,
pub config: Config, pub config: Config,
pub swap_id: Uuid, pub swap_id: Uuid,
pub db: Database, pub db: Database,
} }
pub struct Builder { pub struct Builder<B, M>
where
B: BitcoinWallet + bitcoin::NewAddress,
M: MoneroWallet,
{
swap_id: Uuid, swap_id: Uuid,
identity: Keypair, identity: Keypair,
peer_id: PeerId, peer_id: PeerId,
@ -63,8 +73,8 @@ pub struct Builder {
listen_address: Multiaddr, listen_address: Multiaddr,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
init_params: InitParams, init_params: InitParams,
} }
@ -74,13 +84,17 @@ enum InitParams {
New { swap_amounts: SwapAmounts }, New { swap_amounts: SwapAmounts },
} }
impl Builder { impl<B, M> Builder<B, M>
where
B: BitcoinWallet + bitcoin::NewAddress,
M: MoneroWallet,
{
pub async fn new( pub async fn new(
seed: Seed, seed: Seed,
config: Config, config: Config,
swap_id: Uuid, swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
db_path: PathBuf, db_path: PathBuf,
listen_address: Multiaddr, listen_address: Multiaddr,
) -> Self { ) -> Self {
@ -108,7 +122,7 @@ impl Builder {
} }
} }
pub async fn build(self) -> Result<(Swap, EventLoop)> { pub async fn build(self) -> Result<(Swap<B, M>, EventLoop)> {
match self.init_params { match self.init_params {
InitParams::New { swap_amounts } => { InitParams::New { swap_amounts } => {
let initial_state = self let initial_state = self

View file

@ -2,15 +2,10 @@
//! Alice holds XMR and wishes receive BTC. //! Alice holds XMR and wishes receive BTC.
use crate::{ use crate::{
bitcoin, bitcoin,
bitcoin::{ bitcoin::timelocks::ExpiredTimelocks,
timelocks::ExpiredTimelocks, TransactionBlockHeight, WaitForTransactionFinality,
WatchForRawTransaction,
},
config::Config, config::Config,
database, database,
database::Database, database::Database,
monero,
monero::CreateWalletForOutput,
protocol::{ protocol::{
alice, alice,
alice::{ alice::{
@ -22,7 +17,7 @@ use crate::{
publish_cancel_transaction, wait_for_bitcoin_encrypted_signature, publish_cancel_transaction, wait_for_bitcoin_encrypted_signature,
wait_for_bitcoin_refund, wait_for_locked_bitcoin, wait_for_bitcoin_refund, wait_for_locked_bitcoin,
}, },
AliceState, AliceState, MoneroWallet,
}, },
}, },
}; };
@ -41,6 +36,20 @@ trait Rng: RngCore + CryptoRng + Send {}
impl<T> Rng for T where T: RngCore + CryptoRng + Send {} impl<T> 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 { pub fn is_complete(state: &AliceState) -> bool {
matches!( matches!(
state, state,
@ -51,14 +60,22 @@ pub fn is_complete(state: &AliceState) -> bool {
) )
} }
pub async fn run(swap: alice::Swap) -> Result<AliceState> { pub async fn run<B, M>(swap: alice::Swap<B, M>) -> Result<AliceState>
where
B: BitcoinWallet,
M: MoneroWallet,
{
run_until(swap, is_complete).await run_until(swap, is_complete).await
} }
pub async fn run_until( pub async fn run_until<B, M>(
swap: alice::Swap, swap: alice::Swap<B, M>,
is_target_state: fn(&AliceState) -> bool, is_target_state: fn(&AliceState) -> bool,
) -> Result<AliceState> { ) -> Result<AliceState>
where
B: BitcoinWallet,
M: MoneroWallet,
{
run_until_internal( run_until_internal(
swap.state, swap.state,
is_target_state, is_target_state,
@ -75,16 +92,20 @@ pub async fn run_until(
// State machine driver for swap execution // State machine driver for swap execution
#[async_recursion] #[async_recursion]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn run_until_internal( async fn run_until_internal<B, M>(
state: AliceState, state: AliceState,
is_target_state: fn(&AliceState) -> bool, is_target_state: fn(&AliceState) -> bool,
mut event_loop_handle: EventLoopHandle, mut event_loop_handle: EventLoopHandle,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
config: Config, config: Config,
swap_id: Uuid, swap_id: Uuid,
db: Database, db: Database,
) -> Result<AliceState> { ) -> Result<AliceState>
where
B: BitcoinWallet,
M: MoneroWallet,
{
info!("Current state:{}", state); info!("Current state:{}", state);
if is_target_state(&state) { if is_target_state(&state) {
Ok(state) Ok(state)

View file

@ -42,17 +42,20 @@ pub mod swap;
mod swap_request; mod swap_request;
mod transfer_proof; mod transfer_proof;
pub struct Swap { pub struct Swap<B, M> {
pub state: BobState, pub state: BobState,
pub event_loop_handle: bob::EventLoopHandle, pub event_loop_handle: bob::EventLoopHandle,
pub db: Database, pub db: Database,
pub bitcoin_wallet: Arc<bitcoin::Wallet>, pub bitcoin_wallet: Arc<B>,
pub monero_wallet: Arc<monero::Wallet>, pub monero_wallet: Arc<M>,
pub config: Config, pub config: Config,
pub swap_id: Uuid, pub swap_id: Uuid,
} }
pub struct Builder { pub struct Builder<B, M>
where
B: bitcoin::NewAddress,
{
swap_id: Uuid, swap_id: Uuid,
identity: Keypair, identity: Keypair,
peer_id: PeerId, peer_id: PeerId,
@ -61,8 +64,8 @@ pub struct Builder {
alice_address: Multiaddr, alice_address: Multiaddr,
alice_peer_id: PeerId, alice_peer_id: PeerId,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
init_params: InitParams, init_params: InitParams,
config: Config, config: Config,
@ -73,14 +76,17 @@ enum InitParams {
New { swap_amounts: SwapAmounts }, New { swap_amounts: SwapAmounts },
} }
impl Builder { impl<B, M> Builder<B, M>
where
B: bitcoin::NewAddress,
{
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
seed: Seed, seed: Seed,
db_path: PathBuf, db_path: PathBuf,
swap_id: Uuid, swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
alice_address: Multiaddr, alice_address: Multiaddr,
alice_peer_id: PeerId, alice_peer_id: PeerId,
config: Config, 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<B, M>, bob::EventLoop)> {
match self.init_params { match self.init_params {
InitParams::New { swap_amounts } => { InitParams::New { swap_amounts } => {
let initial_state = self let initial_state = self
@ -164,6 +170,7 @@ impl Builder {
} }
} }
} }
fn init_event_loop( fn init_event_loop(
&self, &self,
) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> { ) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> {

View file

@ -17,6 +17,28 @@ use tokio::select;
use tracing::info; use tracing::info;
use uuid::Uuid; 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 { pub fn is_complete(state: &BobState) -> bool {
matches!( matches!(
state, state,
@ -28,14 +50,22 @@ pub fn is_complete(state: &BobState) -> bool {
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn run(swap: bob::Swap) -> Result<BobState> { pub async fn run<B, M>(swap: bob::Swap<B, M>) -> Result<BobState>
where
B: BitcoinWallet,
M: MoneroWallet,
{
run_until(swap, is_complete).await run_until(swap, is_complete).await
} }
pub async fn run_until( pub async fn run_until<B, M>(
swap: bob::Swap, swap: bob::Swap<B, M>,
is_target_state: fn(&BobState) -> bool, is_target_state: fn(&BobState) -> bool,
) -> Result<BobState> { ) -> Result<BobState>
where
B: BitcoinWallet,
M: MoneroWallet,
{
run_until_internal( run_until_internal(
swap.state, swap.state,
is_target_state, is_target_state,
@ -53,19 +83,21 @@ pub async fn run_until(
// State machine driver for swap execution // State machine driver for swap execution
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[async_recursion] #[async_recursion]
async fn run_until_internal<R>( async fn run_until_internal<R, B, M>(
state: BobState, state: BobState,
is_target_state: fn(&BobState) -> bool, is_target_state: fn(&BobState) -> bool,
mut event_loop_handle: EventLoopHandle, mut event_loop_handle: EventLoopHandle,
db: Database, db: Database,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
mut rng: R, mut rng: R,
swap_id: Uuid, swap_id: Uuid,
config: Config, config: Config,
) -> Result<BobState> ) -> Result<BobState>
where where
R: RngCore + CryptoRng + Send, R: RngCore + CryptoRng + Send,
B: BitcoinWallet,
M: MoneroWallet,
{ {
info!("Current state: {}", state); info!("Current state: {}", state);
if is_target_state(&state) { if is_target_state(&state) {
@ -139,8 +171,7 @@ where
// TODO: This can be optimized further by extracting the block height when // 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 // tx-lock was included. However, scanning a few more blocks won't do any harm
// and is simpler. // and is simpler.
let monero_wallet_restore_blockheight = let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
monero_wallet.inner.block_height().await?;
select! { select! {
transfer_proof = transfer_proof_watcher => { transfer_proof = transfer_proof_watcher => {
@ -387,15 +418,16 @@ where
} }
} }
pub async fn negotiate<R>( pub async fn negotiate<R, W>(
state0: crate::protocol::bob::state::State0, state0: crate::protocol::bob::state::State0,
amounts: SwapAmounts, amounts: SwapAmounts,
swarm: &mut EventLoopHandle, swarm: &mut EventLoopHandle,
mut rng: R, mut rng: R,
bitcoin_wallet: Arc<crate::bitcoin::Wallet>, bitcoin_wallet: Arc<W>,
) -> Result<bob::state::State2> ) -> Result<bob::state::State2>
where where
R: RngCore + CryptoRng + Send, R: RngCore + CryptoRng + Send,
W: bitcoin::BuildTxLockPsbt + bitcoin::GetNetwork,
{ {
tracing::trace!("Starting negotiate"); tracing::trace!("Starting negotiate");
swarm swarm

View file

@ -24,18 +24,26 @@ pub struct StartingBalances {
pub btc: bitcoin::Amount, pub btc: bitcoin::Amount,
} }
struct AliceParams { struct AliceParams<B, M>
where
B: swap::protocol::alice::BitcoinWallet,
M: swap::protocol::alice::MoneroWallet,
{
seed: Seed, seed: Seed,
config: Config, config: Config,
swap_id: Uuid, swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
db_path: PathBuf, db_path: PathBuf,
listen_address: Multiaddr, listen_address: Multiaddr,
} }
impl AliceParams { impl<B, M> AliceParams<B, M>
pub async fn builder(&self) -> alice::Builder { where
B: swap::protocol::alice::BitcoinWallet + bitcoin::NewAddress,
M: swap::protocol::alice::MoneroWallet,
{
pub async fn builder(&self) -> alice::Builder<B, M> {
alice::Builder::new( alice::Builder::new(
self.seed, self.seed,
self.config, self.config,
@ -53,19 +61,22 @@ impl AliceParams {
} }
} }
struct BobParams { struct BobParams<B, M> {
seed: Seed, seed: Seed,
db_path: PathBuf, db_path: PathBuf,
swap_id: Uuid, swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<B>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<M>,
alice_address: Multiaddr, alice_address: Multiaddr,
alice_peer_id: PeerId, alice_peer_id: PeerId,
config: Config, config: Config,
} }
impl BobParams { impl<B, M> BobParams<B, M>
pub fn builder(&self) -> bob::Builder { where
B: bitcoin::NewAddress,
{
pub fn builder(&self) -> bob::Builder<B, M> {
bob::Builder::new( bob::Builder::new(
self.seed, self.seed,
self.db_path.clone(), self.db_path.clone(),
@ -79,22 +90,37 @@ impl BobParams {
} }
} }
pub struct TestContext { pub struct TestContext<BA, MA, BB, MB>
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, swap_amounts: SwapAmounts,
alice_params: AliceParams, alice_params: AliceParams<BA, MA>,
alice_starting_balances: StartingBalances, alice_starting_balances: StartingBalances,
alice_bitcoin_wallet: Arc<bitcoin::Wallet>, alice_bitcoin_wallet: Arc<BA>,
alice_monero_wallet: Arc<monero::Wallet>, alice_monero_wallet: Arc<MA>,
bob_params: BobParams, bob_params: BobParams<BB, MB>,
bob_starting_balances: StartingBalances, bob_starting_balances: StartingBalances,
bob_bitcoin_wallet: Arc<bitcoin::Wallet>, bob_bitcoin_wallet: Arc<BB>,
bob_monero_wallet: Arc<monero::Wallet>, bob_monero_wallet: Arc<MB>,
} }
impl TestContext { impl<BA, MA, BB, MB> TestContext<BA, MA, BB, MB>
pub async fn new_swap_as_alice(&mut self) -> alice::Swap { 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<BA, MA> {
let (swap, mut event_loop) = self let (swap, mut event_loop) = self
.alice_params .alice_params
.builder() .builder()
@ -109,7 +135,7 @@ impl TestContext {
swap swap
} }
pub async fn new_swap_as_bob(&mut self) -> bob::Swap { pub async fn new_swap_as_bob(&mut self) -> bob::Swap<BB, MB> {
let (swap, event_loop) = self let (swap, event_loop) = self
.bob_params .bob_params
.builder() .builder()
@ -123,7 +149,7 @@ impl TestContext {
swap swap
} }
pub async fn recover_alice_from_db(&mut self) -> alice::Swap { pub async fn recover_alice_from_db(&mut self) -> alice::Swap<BA, MA> {
let (swap, mut event_loop) = self.alice_params.builder().await.build().await.unwrap(); let (swap, mut event_loop) = self.alice_params.builder().await.build().await.unwrap();
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
@ -131,7 +157,7 @@ impl TestContext {
swap swap
} }
pub async fn recover_bob_from_db(&mut self) -> bob::Swap { pub async fn recover_bob_from_db(&mut self) -> bob::Swap<BB, MB> {
let (swap, event_loop) = self.bob_params.builder().build().await.unwrap(); let (swap, event_loop) = self.bob_params.builder().build().await.unwrap();
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
@ -149,12 +175,7 @@ impl TestContext {
- bitcoin::Amount::from_sat(bitcoin::TX_FEE) - bitcoin::Amount::from_sat(bitcoin::TX_FEE)
); );
let xmr_balance_after_swap = self let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap();
.alice_monero_wallet
.as_ref()
.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.swap_amounts.xmr);
} }
@ -165,18 +186,8 @@ impl TestContext {
assert_eq!(btc_balance_after_swap, self.alice_starting_balances.btc); 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 // Ensure that Alice's balance is refreshed as we use a newly created wallet
self.alice_monero_wallet self.alice_monero_wallet.as_ref().refresh().await.unwrap();
.as_ref() let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap();
.inner
.refresh()
.await
.unwrap();
let xmr_balance_after_swap = self
.alice_monero_wallet
.as_ref()
.get_balance()
.await
.unwrap();
assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr); assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr);
} }
@ -190,12 +201,7 @@ impl TestContext {
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE) - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE)
); );
let xmr_balance_after_swap = self let xmr_balance_after_swap = self.alice_monero_wallet.as_ref().balance().await.unwrap();
.alice_monero_wallet
.as_ref()
.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.swap_amounts.xmr);
} }
@ -219,13 +225,8 @@ impl TestContext {
); );
// Ensure that Bob's balance is refreshed as we use a newly created wallet // Ensure that Bob's balance is refreshed as we use a newly created wallet
self.bob_monero_wallet self.bob_monero_wallet.as_ref().refresh().await.unwrap();
.as_ref() let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().balance().await.unwrap();
.inner
.refresh()
.await
.unwrap();
let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
assert_eq!( assert_eq!(
xmr_balance_after_swap, xmr_balance_after_swap,
self.bob_starting_balances.xmr + self.swap_amounts.xmr 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 // Since we cannot be sure who submitted it we have to assert accordingly
assert!(alice_submitted_cancel || bob_submitted_cancel); 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); 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 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); assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr);
} }
} }
pub async fn setup_test<T, F>(testfn: T) pub async fn setup_test<T, F>(testfn: T)
where where
T: Fn(TestContext) -> F, T: Fn(TestContext<bitcoin::Wallet, monero::Wallet, bitcoin::Wallet, monero::Wallet>) -> F,
F: Future<Output = ()>, F: Future<Output = ()>,
{ {
let cli = Cli::default(); let cli = Cli::default();