150: Few improvements r=D4nte a=D4nte



Co-authored-by: Franck Royer <franck@coblox.tech>
This commit is contained in:
bors[bot] 2021-01-20 11:30:35 +00:00 committed by GitHub
commit d6bf66c1f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 397 additions and 426 deletions

View File

@ -23,7 +23,7 @@ use swap::{
config::Config, config::Config,
database::Database, database::Database,
monero, monero,
protocol::{alice, bob, bob::SwapFactory, StartingBalances}, protocol::{alice, bob, bob::Builder},
trace::init_tracing, trace::init_tracing,
SwapAmounts, SwapAmounts,
}; };
@ -67,7 +67,7 @@ async fn main() -> Result<()> {
btc: receive_bitcoin, btc: receive_bitcoin,
}; };
let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url, bitcoind_url,
bitcoin_wallet_name.as_str(), bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url, monero_wallet_rpc_url,
@ -82,18 +82,18 @@ async fn main() -> Result<()> {
send_monero, receive_bitcoin, swap_id send_monero, receive_bitcoin, swap_id
); );
let alice_factory = alice::SwapFactory::new( let alice_factory = alice::Builder::new(
seed, seed,
config, config,
swap_id, swap_id,
bitcoin_wallet, Arc::new(bitcoin_wallet),
monero_wallet, Arc::new(monero_wallet),
starting_balances,
db_path, db_path,
listen_addr, listen_addr,
) )
.await; .await;
let (swap, mut event_loop) = alice_factory.new_swap_as_alice(swap_amounts).await?; let (swap, mut event_loop) =
alice_factory.with_init_params(swap_amounts).build().await?;
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
alice::run(swap).await?; alice::run(swap).await?;
@ -112,7 +112,7 @@ async fn main() -> Result<()> {
xmr: receive_monero, xmr: receive_monero,
}; };
let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url, bitcoind_url,
bitcoin_wallet_name.as_str(), bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url, monero_wallet_rpc_url,
@ -127,18 +127,19 @@ async fn main() -> Result<()> {
send_bitcoin, receive_monero, swap_id send_bitcoin, receive_monero, swap_id
); );
let bob_factory = SwapFactory::new( let bob_factory = Builder::new(
seed, seed,
db_path, db_path,
swap_id, swap_id,
bitcoin_wallet, Arc::new(bitcoin_wallet),
monero_wallet, Arc::new(monero_wallet),
config,
starting_balances,
alice_addr, alice_addr,
alice_peer_id, alice_peer_id,
); );
let (swap, event_loop) = bob_factory.new_swap_as_bob(swap_amounts).await?; let (swap, event_loop) = bob_factory
.with_init_params(swap_amounts, config)
.build()
.await?;
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
bob::run(swap).await?; bob::run(swap).await?;
@ -164,7 +165,7 @@ async fn main() -> Result<()> {
monero_wallet_rpc_url, monero_wallet_rpc_url,
listen_addr, listen_addr,
}) => { }) => {
let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url, bitcoind_url,
bitcoin_wallet_name.as_str(), bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url, monero_wallet_rpc_url,
@ -172,18 +173,17 @@ async fn main() -> Result<()> {
) )
.await?; .await?;
let alice_factory = alice::SwapFactory::new( let alice_factory = alice::Builder::new(
seed, seed,
config, config,
swap_id, swap_id,
bitcoin_wallet, Arc::new(bitcoin_wallet),
monero_wallet, Arc::new(monero_wallet),
starting_balances,
db_path, db_path,
listen_addr, listen_addr,
) )
.await; .await;
let (swap, mut event_loop) = alice_factory.recover_alice_from_db().await?; let (swap, mut event_loop) = alice_factory.build().await?;
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
alice::run(swap).await?; alice::run(swap).await?;
@ -196,7 +196,7 @@ async fn main() -> Result<()> {
alice_peer_id, alice_peer_id,
alice_addr, alice_addr,
}) => { }) => {
let (bitcoin_wallet, monero_wallet, starting_balances) = setup_wallets( let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url, bitcoind_url,
bitcoin_wallet_name.as_str(), bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url, monero_wallet_rpc_url,
@ -204,18 +204,16 @@ async fn main() -> Result<()> {
) )
.await?; .await?;
let bob_factory = SwapFactory::new( let bob_factory = Builder::new(
seed, seed,
db_path, db_path,
swap_id, swap_id,
bitcoin_wallet, Arc::new(bitcoin_wallet),
monero_wallet, Arc::new(monero_wallet),
config,
starting_balances,
alice_addr, alice_addr,
alice_peer_id, alice_peer_id,
); );
let (swap, event_loop) = bob_factory.recover_bob_from_db().await?; let (swap, event_loop) = bob_factory.build().await?;
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
bob::run(swap).await?; bob::run(swap).await?;
@ -230,11 +228,7 @@ async fn setup_wallets(
bitcoin_wallet_name: &str, bitcoin_wallet_name: &str,
monero_wallet_rpc_url: url::Url, monero_wallet_rpc_url: url::Url,
config: Config, config: Config,
) -> Result<( ) -> Result<(swap::bitcoin::Wallet, swap::monero::Wallet)> {
Arc<swap::bitcoin::Wallet>,
Arc<swap::monero::Wallet>,
StartingBalances,
)> {
let bitcoin_wallet = let bitcoin_wallet =
swap::bitcoin::Wallet::new(bitcoin_wallet_name, bitcoind_url, config.bitcoin_network) swap::bitcoin::Wallet::new(bitcoin_wallet_name, bitcoind_url, config.bitcoin_network)
.await?; .await?;
@ -243,7 +237,6 @@ async fn setup_wallets(
"Connection to Bitcoin wallet succeeded, balance: {}", "Connection to Bitcoin wallet succeeded, balance: {}",
bitcoin_balance bitcoin_balance
); );
let bitcoin_wallet = Arc::new(bitcoin_wallet);
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.get_balance().await?;
@ -251,12 +244,6 @@ async fn setup_wallets(
"Connection to Monero wallet succeeded, balance: {}", "Connection to Monero wallet succeeded, balance: {}",
monero_balance monero_balance
); );
let monero_wallet = Arc::new(monero_wallet);
let starting_balances = StartingBalances { Ok((bitcoin_wallet, monero_wallet))
btc: bitcoin_balance,
xmr: monero_balance,
};
Ok((bitcoin_wallet, monero_wallet, starting_balances))
} }

View File

@ -24,10 +24,7 @@ pub use self::{
state::*, state::*,
swap::{run, run_until}, swap::{run, run_until},
}; };
use crate::{ use crate::{config::Config, database::Database, network::transport::build, seed::Seed};
config::Config, database::Database, network::transport::build, protocol::StartingBalances,
seed::Seed,
};
use libp2p::{core::Multiaddr, identity::Keypair}; use libp2p::{core::Multiaddr, identity::Keypair};
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
@ -53,7 +50,7 @@ pub struct Swap {
pub db: Database, pub db: Database,
} }
pub struct SwapFactory { pub struct Builder {
swap_id: Uuid, swap_id: Uuid,
identity: Keypair, identity: Keypair,
peer_id: PeerId, peer_id: PeerId,
@ -62,20 +59,24 @@ pub struct SwapFactory {
listen_address: Multiaddr, listen_address: Multiaddr,
pub bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<bitcoin::Wallet>,
pub monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<monero::Wallet>,
pub starting_balances: StartingBalances,
init_params: InitParams,
} }
impl SwapFactory { enum InitParams {
#[allow(clippy::too_many_arguments)] None,
New { swap_amounts: SwapAmounts },
}
impl Builder {
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<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<monero::Wallet>,
starting_balances: StartingBalances,
db_path: PathBuf, db_path: PathBuf,
listen_address: Multiaddr, listen_address: Multiaddr,
) -> Self { ) -> Self {
@ -92,32 +93,33 @@ impl SwapFactory {
listen_address, listen_address,
bitcoin_wallet, bitcoin_wallet,
monero_wallet, monero_wallet,
starting_balances, init_params: InitParams::None,
} }
} }
pub async fn new_swap_as_alice(&self, swap_amounts: SwapAmounts) -> Result<(Swap, EventLoop)> { pub fn with_init_params(self, swap_amounts: SwapAmounts) -> Self {
let initial_state = init_alice_state( Self {
swap_amounts.btc, init_params: InitParams::New { swap_amounts },
swap_amounts.xmr, ..self
self.bitcoin_wallet.clone(), }
self.config, }
)
pub async fn build(self) -> Result<(Swap, EventLoop)> {
match self.init_params {
InitParams::New { swap_amounts } => {
let initial_state = self
.make_initial_state(swap_amounts.btc, swap_amounts.xmr)
.await?; .await?;
let (event_loop, event_loop_handle) = init_alice_event_loop( let (event_loop, event_loop_handle) = self.init_event_loop()?;
self.listen_address.clone(),
self.identity.clone(),
self.peer_id.clone(),
)?;
let db = Database::open(self.db_path.as_path())?; let db = Database::open(self.db_path.as_path())?;
Ok(( Ok((
Swap { Swap {
event_loop_handle, event_loop_handle,
bitcoin_wallet: self.bitcoin_wallet.clone(), bitcoin_wallet: self.bitcoin_wallet,
monero_wallet: self.monero_wallet.clone(), monero_wallet: self.monero_wallet,
config: self.config, config: self.config,
db, db,
state: initial_state, state: initial_state,
@ -126,12 +128,12 @@ impl SwapFactory {
event_loop, event_loop,
)) ))
} }
InitParams::None => {
pub async fn recover_alice_from_db(&self) -> Result<(Swap, EventLoop)> {
// reopen the existing database // reopen the existing database
let db = Database::open(self.db_path.clone().as_path())?; let db = Database::open(self.db_path.as_path())?;
let resume_state = if let database::Swap::Alice(state) = db.get_state(self.swap_id)? { let resume_state =
if let database::Swap::Alice(state) = db.get_state(self.swap_id)? {
state.into() state.into()
} else { } else {
bail!( bail!(
@ -140,18 +142,14 @@ impl SwapFactory {
) )
}; };
let (event_loop, event_loop_handle) = init_alice_event_loop( let (event_loop, event_loop_handle) = self.init_event_loop()?;
self.listen_address.clone(),
self.identity.clone(),
self.peer_id.clone(),
)?;
Ok(( Ok((
Swap { Swap {
state: resume_state, state: resume_state,
event_loop_handle, event_loop_handle,
bitcoin_wallet: self.bitcoin_wallet.clone(), bitcoin_wallet: self.bitcoin_wallet,
monero_wallet: self.monero_wallet.clone(), monero_wallet: self.monero_wallet,
config: self.config, config: self.config,
swap_id: self.swap_id, swap_id: self.swap_id,
db, db,
@ -159,6 +157,8 @@ impl SwapFactory {
event_loop, event_loop,
)) ))
} }
}
}
pub fn peer_id(&self) -> PeerId { pub fn peer_id(&self) -> PeerId {
self.peer_id.clone() self.peer_id.clone()
@ -167,14 +167,12 @@ impl SwapFactory {
pub fn listen_address(&self) -> Multiaddr { pub fn listen_address(&self) -> Multiaddr {
self.listen_address.clone() self.listen_address.clone()
} }
}
async fn init_alice_state( async fn make_initial_state(
&self,
btc_to_swap: bitcoin::Amount, btc_to_swap: bitcoin::Amount,
xmr_to_swap: monero::Amount, xmr_to_swap: monero::Amount,
alice_btc_wallet: Arc<bitcoin::Wallet>, ) -> Result<AliceState> {
config: Config,
) -> Result<AliceState> {
let rng = &mut OsRng; let rng = &mut OsRng;
let amounts = SwapAmounts { let amounts = SwapAmounts {
@ -185,7 +183,7 @@ async fn init_alice_state(
let a = bitcoin::SecretKey::new_random(rng); let a = bitcoin::SecretKey::new_random(rng);
let s_a = cross_curve_dleq::Scalar::random(rng); let s_a = cross_curve_dleq::Scalar::random(rng);
let v_a = monero::PrivateViewKey::new_random(rng); let v_a = monero::PrivateViewKey::new_random(rng);
let redeem_address = alice_btc_wallet.as_ref().new_address().await?; let redeem_address = self.bitcoin_wallet.new_address().await?;
let punish_address = redeem_address.clone(); let punish_address = redeem_address.clone();
let state0 = State0::new( let state0 = State0::new(
a, a,
@ -193,23 +191,25 @@ async fn init_alice_state(
v_a, v_a,
amounts.btc, amounts.btc,
amounts.xmr, amounts.xmr,
config.bitcoin_cancel_timelock, self.config.bitcoin_cancel_timelock,
config.bitcoin_punish_timelock, self.config.bitcoin_punish_timelock,
redeem_address, redeem_address,
punish_address, punish_address,
); );
Ok(AliceState::Started { amounts, state0 }) Ok(AliceState::Started { amounts, state0 })
} }
fn init_alice_event_loop( fn init_event_loop(&self) -> Result<(EventLoop, EventLoopHandle)> {
listen: Multiaddr,
identity: Keypair,
peer_id: PeerId,
) -> Result<(EventLoop, EventLoopHandle)> {
let alice_behaviour = Behaviour::default(); let alice_behaviour = Behaviour::default();
let alice_transport = build(identity)?; let alice_transport = build(self.identity.clone())?;
EventLoop::new(alice_transport, alice_behaviour, listen, peer_id) EventLoop::new(
alice_transport,
alice_behaviour,
self.listen_address.clone(),
self.peer_id.clone(),
)
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -23,10 +23,7 @@ pub use self::{
state::*, state::*,
swap::{run, run_until}, swap::{run, run_until},
}; };
use crate::{ use crate::{config::Config, database::Database, network::transport::build, seed::Seed};
config::Config, database::Database, network::transport::build, protocol::StartingBalances,
seed::Seed,
};
use libp2p::identity::Keypair; use libp2p::identity::Keypair;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
@ -50,33 +47,38 @@ pub struct Swap {
pub swap_id: Uuid, pub swap_id: Uuid,
} }
pub struct SwapFactory { pub struct Builder {
swap_id: Uuid, swap_id: Uuid,
identity: Keypair, identity: Keypair,
peer_id: PeerId, peer_id: PeerId,
db_path: PathBuf, db_path: PathBuf,
config: Config,
alice_connect_address: Multiaddr, alice_address: Multiaddr,
alice_connect_peer_id: PeerId, alice_peer_id: PeerId,
pub bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<bitcoin::Wallet>,
pub monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<monero::Wallet>,
pub starting_balances: StartingBalances,
init_params: InitParams,
} }
impl SwapFactory { enum InitParams {
#[allow(clippy::too_many_arguments)] None,
New {
swap_amounts: SwapAmounts,
config: Config,
},
}
impl Builder {
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<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>, monero_wallet: Arc<monero::Wallet>,
config: Config, alice_address: Multiaddr,
starting_balances: StartingBalances, alice_peer_id: PeerId,
alice_connect_address: Multiaddr,
alice_connect_peer_id: PeerId,
) -> Self { ) -> Self {
let identity = network::Seed::new(seed).derive_libp2p_identity(); let identity = network::Seed::new(seed).derive_libp2p_identity();
let peer_id = identity.public().into_peer_id(); let peer_id = identity.public().into_peer_id();
@ -86,33 +88,35 @@ impl SwapFactory {
identity, identity,
peer_id, peer_id,
db_path, db_path,
config, alice_address,
alice_connect_address, alice_peer_id,
alice_connect_peer_id,
bitcoin_wallet, bitcoin_wallet,
monero_wallet, monero_wallet,
starting_balances, init_params: InitParams::None,
} }
} }
pub async fn new_swap_as_bob( pub fn with_init_params(self, swap_amounts: SwapAmounts, config: Config) -> Self {
&self, Self {
swap_amounts: SwapAmounts, init_params: InitParams::New {
) -> Result<(bob::Swap, bob::EventLoop)> { swap_amounts,
let initial_state = init_bob_state( config,
swap_amounts.btc, },
swap_amounts.xmr, ..self
self.bitcoin_wallet.clone(), }
self.config, }
)
pub async fn build(self) -> Result<(bob::Swap, bob::EventLoop)> {
match self.init_params {
InitParams::New {
swap_amounts,
config,
} => {
let initial_state = self
.make_initial_state(swap_amounts.btc, swap_amounts.xmr, config)
.await?; .await?;
let (event_loop, event_loop_handle) = init_bob_event_loop( let (event_loop, event_loop_handle) = self.init_event_loop()?;
self.identity.clone(),
self.peer_id.clone(),
self.alice_connect_peer_id.clone(),
self.alice_connect_address.clone(),
)?;
let db = Database::open(self.db_path.as_path())?; let db = Database::open(self.db_path.as_path())?;
@ -128,10 +132,9 @@ impl SwapFactory {
event_loop, event_loop,
)) ))
} }
InitParams::None => {
pub async fn recover_bob_from_db(&self) -> Result<(bob::Swap, bob::EventLoop)> {
// reopen the existing database // reopen the existing database
let db = Database::open(self.db_path.clone().as_path())?; let db = Database::open(self.db_path.as_path())?;
let resume_state = if let database::Swap::Bob(state) = db.get_state(self.swap_id)? { let resume_state = if let database::Swap::Bob(state) = db.get_state(self.swap_id)? {
state.into() state.into()
@ -142,12 +145,7 @@ impl SwapFactory {
) )
}; };
let (event_loop, event_loop_handle) = init_bob_event_loop( let (event_loop, event_loop_handle) = self.init_event_loop()?;
self.identity.clone(),
self.peer_id.clone(),
self.alice_connect_peer_id.clone(),
self.alice_connect_address.clone(),
)?;
Ok(( Ok((
Swap { Swap {
@ -161,20 +159,35 @@ impl SwapFactory {
event_loop, event_loop,
)) ))
} }
} }
}
fn init_event_loop(
&self,
) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> {
let bob_behaviour = bob::Behaviour::default();
let bob_transport = build(self.identity.clone())?;
async fn init_bob_state( bob::event_loop::EventLoop::new(
bob_transport,
bob_behaviour,
self.peer_id.clone(),
self.alice_peer_id.clone(),
self.alice_address.clone(),
)
}
async fn make_initial_state(
&self,
btc_to_swap: bitcoin::Amount, btc_to_swap: bitcoin::Amount,
xmr_to_swap: monero::Amount, xmr_to_swap: monero::Amount,
bob_btc_wallet: Arc<bitcoin::Wallet>,
config: Config, config: Config,
) -> Result<BobState> { ) -> Result<BobState> {
let amounts = SwapAmounts { let amounts = SwapAmounts {
btc: btc_to_swap, btc: btc_to_swap,
xmr: xmr_to_swap, xmr: xmr_to_swap,
}; };
let refund_address = bob_btc_wallet.new_address().await?; let refund_address = self.bitcoin_wallet.new_address().await?;
let state0 = bob::State0::new( let state0 = bob::State0::new(
&mut OsRng, &mut OsRng,
btc_to_swap, btc_to_swap,
@ -186,24 +199,7 @@ async fn init_bob_state(
); );
Ok(BobState::Started { state0, amounts }) Ok(BobState::Started { state0, amounts })
} }
fn init_bob_event_loop(
identity: Keypair,
peer_id: PeerId,
alice_peer_id: PeerId,
alice_addr: Multiaddr,
) -> Result<(bob::event_loop::EventLoop, bob::event_loop::EventLoopHandle)> {
let bob_behaviour = bob::Behaviour::default();
let bob_transport = build(identity)?;
bob::event_loop::EventLoop::new(
bob_transport,
bob_behaviour,
peer_id,
alice_peer_id,
alice_addr,
)
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -7,7 +7,7 @@ pub mod testutils;
#[tokio::test] #[tokio::test]
async fn happy_path() { async fn happy_path() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -4,7 +4,7 @@ pub mod testutils;
#[tokio::test] #[tokio::test]
async fn given_alice_restarts_after_encsig_is_learned_resume_swap() { async fn given_alice_restarts_after_encsig_is_learned_resume_swap() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -4,7 +4,7 @@ pub mod testutils;
#[tokio::test] #[tokio::test]
async fn given_bob_restarts_after_encsig_is_sent_resume_swap() { async fn given_bob_restarts_after_encsig_is_sent_resume_swap() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -7,7 +7,7 @@ pub mod testutils;
#[tokio::test] #[tokio::test]
async fn given_bob_restarts_after_xmr_is_locked_resume_swap() { async fn given_bob_restarts_after_xmr_is_locked_resume_swap() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -9,7 +9,7 @@ pub mod testutils;
/// the encsig and fail to refund or redeem. Alice punishes. /// the encsig and fail to refund or redeem. Alice punishes.
#[tokio::test] #[tokio::test]
async fn alice_punishes_if_bob_never_acts_after_fund() { async fn alice_punishes_if_bob_never_acts_after_fund() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -6,7 +6,7 @@ pub mod testutils;
/// then also refunds. /// then also refunds.
#[tokio::test] #[tokio::test]
async fn given_alice_restarts_after_xmr_is_locked_abort_swap() { async fn given_alice_restarts_after_xmr_is_locked_abort_swap() {
testutils::setup_test(|ctx| async move { testutils::setup_test(|mut ctx| async move {
let alice_swap = ctx.new_swap_as_alice().await; let alice_swap = ctx.new_swap_as_alice().await;
let bob_swap = ctx.new_swap_as_bob().await; let bob_swap = ctx.new_swap_as_bob().await;

View File

@ -2,14 +2,14 @@ use crate::testutils;
use bitcoin_harness::Bitcoind; use bitcoin_harness::Bitcoind;
use futures::Future; use futures::Future;
use get_port::get_port; use get_port::get_port;
use libp2p::core::Multiaddr; use libp2p::{core::Multiaddr, PeerId};
use monero_harness::{image, Monero}; use monero_harness::{image, Monero};
use std::sync::Arc; use std::{path::PathBuf, sync::Arc};
use swap::{ use swap::{
bitcoin, bitcoin,
config::Config, config::Config,
monero, monero,
protocol::{alice, alice::AliceState, bob, bob::BobState, StartingBalances}, protocol::{alice, alice::AliceState, bob, bob::BobState},
seed::Seed, seed::Seed,
SwapAmounts, SwapAmounts,
}; };
@ -19,18 +19,87 @@ use tracing_core::dispatcher::DefaultGuard;
use tracing_log::LogTracer; use tracing_log::LogTracer;
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct StartingBalances {
pub xmr: monero::Amount,
pub btc: bitcoin::Amount,
}
struct AliceParams {
seed: Seed,
config: Config,
swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
db_path: PathBuf,
listen_address: Multiaddr,
}
impl AliceParams {
pub async fn builder(&self) -> alice::Builder {
alice::Builder::new(
self.seed,
self.config,
self.swap_id,
self.bitcoin_wallet.clone(),
self.monero_wallet.clone(),
self.db_path.clone(),
self.listen_address.clone(),
)
.await
}
async fn peer_id(&self) -> PeerId {
self.builder().await.peer_id()
}
}
struct BobParams {
seed: Seed,
db_path: PathBuf,
swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
alice_address: Multiaddr,
alice_peer_id: PeerId,
}
impl BobParams {
pub fn builder(&self) -> bob::Builder {
bob::Builder::new(
self.seed,
self.db_path.clone(),
self.swap_id,
self.bitcoin_wallet.clone(),
self.monero_wallet.clone(),
self.alice_address.clone(),
self.alice_peer_id.clone(),
)
}
}
pub struct TestContext { pub struct TestContext {
swap_amounts: SwapAmounts, swap_amounts: SwapAmounts,
alice_swap_factory: alice::SwapFactory, alice_params: AliceParams,
bob_swap_factory: bob::SwapFactory, alice_starting_balances: StartingBalances,
alice_bitcoin_wallet: Arc<bitcoin::Wallet>,
alice_monero_wallet: Arc<monero::Wallet>,
bob_params: BobParams,
bob_starting_balances: StartingBalances,
bob_bitcoin_wallet: Arc<bitcoin::Wallet>,
bob_monero_wallet: Arc<monero::Wallet>,
} }
impl TestContext { impl TestContext {
pub async fn new_swap_as_alice(&self) -> alice::Swap { pub async fn new_swap_as_alice(&mut self) -> alice::Swap {
let (swap, mut event_loop) = self let (swap, mut event_loop) = self
.alice_swap_factory .alice_params
.new_swap_as_alice(self.swap_amounts) .builder()
.await
.with_init_params(self.swap_amounts)
.build()
.await .await
.unwrap(); .unwrap();
@ -39,10 +108,12 @@ impl TestContext {
swap swap
} }
pub async fn new_swap_as_bob(&self) -> bob::Swap { pub async fn new_swap_as_bob(&mut self) -> bob::Swap {
let (swap, event_loop) = self let (swap, event_loop) = self
.bob_swap_factory .bob_params
.new_swap_as_bob(self.swap_amounts) .builder()
.with_init_params(self.swap_amounts, Config::regtest())
.build()
.await .await
.unwrap(); .unwrap();
@ -51,20 +122,16 @@ impl TestContext {
swap swap
} }
pub async fn recover_alice_from_db(&self) -> alice::Swap { pub async fn recover_alice_from_db(&mut self) -> alice::Swap {
let (swap, mut event_loop) = self let (swap, mut event_loop) = self.alice_params.builder().await.build().await.unwrap();
.alice_swap_factory
.recover_alice_from_db()
.await
.unwrap();
tokio::spawn(async move { event_loop.run().await }); tokio::spawn(async move { event_loop.run().await });
swap swap
} }
pub async fn recover_bob_from_db(&self) -> bob::Swap { pub async fn recover_bob_from_db(&mut self) -> bob::Swap {
let (swap, event_loop) = self.bob_swap_factory.recover_bob_from_db().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 });
@ -74,58 +141,37 @@ impl TestContext {
pub async fn assert_alice_redeemed(&self, state: AliceState) { pub async fn assert_alice_redeemed(&self, state: AliceState) {
assert!(matches!(state, AliceState::BtcRedeemed)); assert!(matches!(state, AliceState::BtcRedeemed));
let btc_balance_after_swap = self let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
.alice_swap_factory
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
assert_eq!( assert_eq!(
btc_balance_after_swap, btc_balance_after_swap,
self.alice_swap_factory.starting_balances.btc + self.swap_amounts.btc self.alice_starting_balances.btc + self.swap_amounts.btc
- 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_swap_factory .alice_monero_wallet
.monero_wallet
.as_ref() .as_ref()
.get_balance() .get_balance()
.await .await
.unwrap(); .unwrap();
assert!( assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr);
xmr_balance_after_swap
<= self.alice_swap_factory.starting_balances.xmr - self.swap_amounts.xmr
);
} }
pub async fn assert_alice_refunded(&self, state: AliceState) { pub async fn assert_alice_refunded(&self, state: AliceState) {
assert!(matches!(state, AliceState::XmrRefunded)); assert!(matches!(state, AliceState::XmrRefunded));
let btc_balance_after_swap = self let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
.alice_swap_factory assert_eq!(btc_balance_after_swap, self.alice_starting_balances.btc);
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
assert_eq!(
btc_balance_after_swap,
self.alice_swap_factory.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_swap_factory self.alice_monero_wallet
.monero_wallet
.as_ref() .as_ref()
.inner .inner
.refresh() .refresh()
.await .await
.unwrap(); .unwrap();
let xmr_balance_after_swap = self let xmr_balance_after_swap = self
.alice_swap_factory .alice_monero_wallet
.monero_wallet
.as_ref() .as_ref()
.get_balance() .get_balance()
.await .await
@ -136,30 +182,20 @@ impl TestContext {
pub async fn assert_alice_punished(&self, state: AliceState) { pub async fn assert_alice_punished(&self, state: AliceState) {
assert!(matches!(state, AliceState::BtcPunished)); assert!(matches!(state, AliceState::BtcPunished));
let btc_balance_after_swap = self let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
.alice_swap_factory
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
assert_eq!( assert_eq!(
btc_balance_after_swap, btc_balance_after_swap,
self.alice_swap_factory.starting_balances.btc + self.swap_amounts.btc self.alice_starting_balances.btc + self.swap_amounts.btc
- 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_swap_factory .alice_monero_wallet
.monero_wallet
.as_ref() .as_ref()
.get_balance() .get_balance()
.await .await
.unwrap(); .unwrap();
assert!( assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.swap_amounts.xmr);
xmr_balance_after_swap
<= self.alice_swap_factory.starting_balances.xmr - self.swap_amounts.xmr
);
} }
pub async fn assert_bob_redeemed(&self, state: BobState) { pub async fn assert_bob_redeemed(&self, state: BobState) {
@ -170,44 +206,28 @@ impl TestContext {
}; };
let lock_tx_bitcoin_fee = self let lock_tx_bitcoin_fee = self
.bob_swap_factory .bob_bitcoin_wallet
.bitcoin_wallet
.transaction_fee(lock_tx_id) .transaction_fee(lock_tx_id)
.await .await
.unwrap(); .unwrap();
let btc_balance_after_swap = self let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
.bob_swap_factory
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
assert_eq!( assert_eq!(
btc_balance_after_swap, btc_balance_after_swap,
self.bob_swap_factory.starting_balances.btc self.bob_starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee
- self.swap_amounts.btc
- lock_tx_bitcoin_fee
); );
// 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_swap_factory self.bob_monero_wallet
.monero_wallet
.as_ref() .as_ref()
.inner .inner
.refresh() .refresh()
.await .await
.unwrap(); .unwrap();
let xmr_balance_after_swap = self let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
.bob_swap_factory
.monero_wallet
.as_ref()
.get_balance()
.await
.unwrap();
assert_eq!( assert_eq!(
xmr_balance_after_swap, xmr_balance_after_swap,
self.bob_swap_factory.starting_balances.xmr + self.swap_amounts.xmr self.bob_starting_balances.xmr + self.swap_amounts.xmr
); );
} }
@ -218,27 +238,20 @@ impl TestContext {
panic!("Bob in unexpected state"); panic!("Bob in unexpected state");
}; };
let lock_tx_bitcoin_fee = self let lock_tx_bitcoin_fee = self
.bob_swap_factory .bob_bitcoin_wallet
.bitcoin_wallet
.transaction_fee(lock_tx_id) .transaction_fee(lock_tx_id)
.await .await
.unwrap(); .unwrap();
let btc_balance_after_swap = self let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
.bob_swap_factory
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
let alice_submitted_cancel = btc_balance_after_swap let alice_submitted_cancel = btc_balance_after_swap
== self.bob_swap_factory.starting_balances.btc == self.bob_starting_balances.btc
- lock_tx_bitcoin_fee - lock_tx_bitcoin_fee
- bitcoin::Amount::from_sat(bitcoin::TX_FEE); - bitcoin::Amount::from_sat(bitcoin::TX_FEE);
let bob_submitted_cancel = btc_balance_after_swap let bob_submitted_cancel = btc_balance_after_swap
== self.bob_swap_factory.starting_balances.btc == self.bob_starting_balances.btc
- lock_tx_bitcoin_fee - lock_tx_bitcoin_fee
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE); - bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE);
@ -246,17 +259,8 @@ 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 let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
.bob_swap_factory assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr);
.monero_wallet
.as_ref()
.get_balance()
.await
.unwrap();
assert_eq!(
xmr_balance_after_swap,
self.bob_swap_factory.starting_balances.xmr
);
} }
pub async fn assert_bob_punished(&self, state: BobState) { pub async fn assert_bob_punished(&self, state: BobState) {
@ -267,37 +271,19 @@ impl TestContext {
}; };
let lock_tx_bitcoin_fee = self let lock_tx_bitcoin_fee = self
.bob_swap_factory .bob_bitcoin_wallet
.bitcoin_wallet
.transaction_fee(lock_tx_id) .transaction_fee(lock_tx_id)
.await .await
.unwrap(); .unwrap();
let btc_balance_after_swap = self let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
.bob_swap_factory
.bitcoin_wallet
.as_ref()
.balance()
.await
.unwrap();
assert_eq!( assert_eq!(
btc_balance_after_swap, btc_balance_after_swap,
self.bob_swap_factory.starting_balances.btc self.bob_starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee
- self.swap_amounts.btc
- lock_tx_bitcoin_fee
); );
let xmr_balance_after_swap = self let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
.bob_swap_factory assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr);
.monero_wallet
.as_ref()
.get_balance()
.await
.unwrap();
assert_eq!(
xmr_balance_after_swap,
self.bob_swap_factory.starting_balances.xmr
);
} }
} }
@ -339,17 +325,15 @@ where
) )
.await; .await;
let alice_swap_factory = alice::SwapFactory::new( let alice_params = AliceParams {
Seed::random().unwrap(), seed: Seed::random().unwrap(),
config, config,
Uuid::new_v4(), swap_id: Uuid::new_v4(),
alice_bitcoin_wallet, bitcoin_wallet: alice_bitcoin_wallet.clone(),
alice_monero_wallet, monero_wallet: alice_monero_wallet.clone(),
alice_starting_balances, db_path: tempdir().unwrap().path().to_path_buf(),
tempdir().unwrap().path().to_path_buf(),
listen_address, listen_address,
) };
.await;
let bob_starting_balances = StartingBalances { let bob_starting_balances = StartingBalances {
xmr: monero::Amount::ZERO, xmr: monero::Amount::ZERO,
@ -365,22 +349,26 @@ where
) )
.await; .await;
let bob_swap_factory = bob::SwapFactory::new( let bob_params = BobParams {
Seed::random().unwrap(), seed: Seed::random().unwrap(),
tempdir().unwrap().path().to_path_buf(), db_path: tempdir().unwrap().path().to_path_buf(),
Uuid::new_v4(), swap_id: Uuid::new_v4(),
bob_bitcoin_wallet, bitcoin_wallet: bob_bitcoin_wallet.clone(),
bob_monero_wallet, monero_wallet: bob_monero_wallet.clone(),
config, alice_address: alice_params.listen_address.clone(),
bob_starting_balances, alice_peer_id: alice_params.peer_id().await,
alice_swap_factory.listen_address(), };
alice_swap_factory.peer_id(),
);
let test = TestContext { let test = TestContext {
swap_amounts, swap_amounts,
alice_swap_factory, alice_params,
bob_swap_factory, alice_starting_balances,
alice_bitcoin_wallet,
alice_monero_wallet,
bob_params,
bob_starting_balances,
bob_bitcoin_wallet,
bob_monero_wallet,
}; };
testfn(test).await testfn(test).await