mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-25 15:39:25 -05:00
Make the factory code usable in production
- Introduce Test abstraction instead of tow harnesses, move test specific data into Test - Change the abstraction from actors to swap, because we are creating swaps, not actors - rename actor::swap to run, because we are running a swap
This commit is contained in:
parent
e4795fa4ee
commit
8bf467b550
@ -300,15 +300,17 @@ async fn alice_swap(
|
|||||||
let (mut event_loop, handle) =
|
let (mut event_loop, handle) =
|
||||||
alice::event_loop::EventLoop::new(alice_transport, alice_behaviour, listen_addr)?;
|
alice::event_loop::EventLoop::new(alice_transport, alice_behaviour, listen_addr)?;
|
||||||
|
|
||||||
let swap = alice::swap::swap(
|
let swap = alice::Swap {
|
||||||
state,
|
state,
|
||||||
handle,
|
event_loop_handle: handle,
|
||||||
bitcoin_wallet.clone(),
|
bitcoin_wallet,
|
||||||
monero_wallet.clone(),
|
monero_wallet,
|
||||||
config,
|
config,
|
||||||
swap_id,
|
swap_id,
|
||||||
db,
|
db,
|
||||||
);
|
};
|
||||||
|
|
||||||
|
let swap = alice::swap::run(swap);
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
swap.await
|
swap.await
|
||||||
@ -331,15 +333,16 @@ async fn bob_swap(
|
|||||||
let (event_loop, handle) =
|
let (event_loop, handle) =
|
||||||
bob::event_loop::EventLoop::new(bob_transport, bob_behaviour, alice_peer_id, alice_addr)?;
|
bob::event_loop::EventLoop::new(bob_transport, bob_behaviour, alice_peer_id, alice_addr)?;
|
||||||
|
|
||||||
let swap = bob::swap::swap(
|
let swap = bob::Swap {
|
||||||
state,
|
state,
|
||||||
handle,
|
event_loop_handle: handle,
|
||||||
db,
|
db,
|
||||||
bitcoin_wallet.clone(),
|
bitcoin_wallet,
|
||||||
monero_wallet.clone(),
|
monero_wallet,
|
||||||
OsRng,
|
|
||||||
swap_id,
|
swap_id,
|
||||||
);
|
};
|
||||||
|
|
||||||
|
let swap = bob::swap::run(swap);
|
||||||
|
|
||||||
tokio::spawn(event_loop.run());
|
tokio::spawn(event_loop.run());
|
||||||
swap.await
|
swap.await
|
||||||
|
@ -9,6 +9,7 @@ use libp2p::{
|
|||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bitcoin, monero,
|
||||||
network::{
|
network::{
|
||||||
peer_tracker::{self, PeerTracker},
|
peer_tracker::{self, PeerTracker},
|
||||||
request_response::AliceToBob,
|
request_response::AliceToBob,
|
||||||
@ -26,8 +27,11 @@ pub use self::{
|
|||||||
message1::Message1,
|
message1::Message1,
|
||||||
message2::Message2,
|
message2::Message2,
|
||||||
state::*,
|
state::*,
|
||||||
swap::{run_until, swap},
|
swap::{run, run_until},
|
||||||
};
|
};
|
||||||
|
use crate::{config::Config, database::Database};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod amounts;
|
mod amounts;
|
||||||
pub mod event_loop;
|
pub mod event_loop;
|
||||||
@ -39,6 +43,16 @@ pub mod state;
|
|||||||
mod steps;
|
mod steps;
|
||||||
pub mod swap;
|
pub mod swap;
|
||||||
|
|
||||||
|
pub struct Swap {
|
||||||
|
pub state: AliceState,
|
||||||
|
pub event_loop_handle: EventLoopHandle,
|
||||||
|
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
|
pub monero_wallet: Arc<monero::Wallet>,
|
||||||
|
pub config: Config,
|
||||||
|
pub swap_id: Uuid,
|
||||||
|
pub db: Database,
|
||||||
|
}
|
||||||
|
|
||||||
pub type Swarm = libp2p::Swarm<Behaviour>;
|
pub type Swarm = libp2p::Swarm<Behaviour>;
|
||||||
|
|
||||||
pub fn new_swarm(
|
pub fn new_swarm(
|
||||||
|
@ -15,19 +15,24 @@ use crate::{
|
|||||||
bitcoin,
|
bitcoin,
|
||||||
bitcoin::{TransactionBlockHeight, WatchForRawTransaction},
|
bitcoin::{TransactionBlockHeight, WatchForRawTransaction},
|
||||||
config::Config,
|
config::Config,
|
||||||
database::{Database, Swap},
|
database,
|
||||||
|
database::Database,
|
||||||
monero,
|
monero,
|
||||||
monero::CreateWalletForOutput,
|
monero::CreateWalletForOutput,
|
||||||
protocol::alice::{
|
protocol::{
|
||||||
|
alice,
|
||||||
|
alice::{
|
||||||
event_loop::EventLoopHandle,
|
event_loop::EventLoopHandle,
|
||||||
steps::{
|
steps::{
|
||||||
build_bitcoin_punish_transaction, build_bitcoin_redeem_transaction,
|
build_bitcoin_punish_transaction, build_bitcoin_redeem_transaction,
|
||||||
extract_monero_private_key, lock_xmr, negotiate, publish_bitcoin_punish_transaction,
|
extract_monero_private_key, lock_xmr, negotiate,
|
||||||
publish_bitcoin_redeem_transaction, publish_cancel_transaction,
|
publish_bitcoin_punish_transaction, publish_bitcoin_redeem_transaction,
|
||||||
wait_for_bitcoin_encrypted_signature, wait_for_bitcoin_refund, wait_for_locked_bitcoin,
|
publish_cancel_transaction, wait_for_bitcoin_encrypted_signature,
|
||||||
|
wait_for_bitcoin_refund, wait_for_locked_bitcoin,
|
||||||
},
|
},
|
||||||
AliceState,
|
AliceState,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
ExpiredTimelocks,
|
ExpiredTimelocks,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,28 +40,6 @@ 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 async fn swap(
|
|
||||||
state: AliceState,
|
|
||||||
event_loop_handle: EventLoopHandle,
|
|
||||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
|
||||||
config: Config,
|
|
||||||
swap_id: Uuid,
|
|
||||||
db: Database,
|
|
||||||
) -> Result<AliceState> {
|
|
||||||
run_until(
|
|
||||||
state,
|
|
||||||
is_complete,
|
|
||||||
event_loop_handle,
|
|
||||||
bitcoin_wallet,
|
|
||||||
monero_wallet,
|
|
||||||
config,
|
|
||||||
swap_id,
|
|
||||||
db,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_complete(state: &AliceState) -> bool {
|
pub fn is_complete(state: &AliceState) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
state,
|
state,
|
||||||
@ -81,10 +64,31 @@ pub fn is_encsig_learned(state: &AliceState) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn run(swap: alice::Swap) -> Result<AliceState> {
|
||||||
|
run_until(swap, is_complete).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_until(
|
||||||
|
swap: alice::Swap,
|
||||||
|
is_target_state: fn(&AliceState) -> bool,
|
||||||
|
) -> Result<AliceState> {
|
||||||
|
do_run_until(
|
||||||
|
swap.state,
|
||||||
|
is_target_state,
|
||||||
|
swap.event_loop_handle,
|
||||||
|
swap.bitcoin_wallet,
|
||||||
|
swap.monero_wallet,
|
||||||
|
swap.config,
|
||||||
|
swap.swap_id,
|
||||||
|
swap.db,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
// 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)]
|
||||||
pub async fn run_until(
|
async fn do_run_until(
|
||||||
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,
|
||||||
@ -110,9 +114,9 @@ pub async fn run_until(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -153,9 +157,9 @@ pub async fn run_until(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -194,9 +198,9 @@ pub async fn run_until(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -232,9 +236,9 @@ pub async fn run_until(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -270,9 +274,9 @@ pub async fn run_until(
|
|||||||
|
|
||||||
let state = AliceState::CancelTimelockExpired { state3 };
|
let state = AliceState::CancelTimelockExpired { state3 };
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
return run_until(
|
return do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -298,9 +302,9 @@ pub async fn run_until(
|
|||||||
|
|
||||||
let state = AliceState::BtcRedeemed;
|
let state = AliceState::BtcRedeemed;
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -325,9 +329,9 @@ pub async fn run_until(
|
|||||||
|
|
||||||
let state = AliceState::BtcCancelled { state3, tx_cancel };
|
let state = AliceState::BtcCancelled { state3, tx_cancel };
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -358,10 +362,10 @@ pub async fn run_until(
|
|||||||
None => {
|
None => {
|
||||||
let state = AliceState::BtcPunishable { tx_refund, state3 };
|
let state = AliceState::BtcPunishable { tx_refund, state3 };
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -384,9 +388,9 @@ pub async fn run_until(
|
|||||||
|
|
||||||
let state = AliceState::BtcRefunded { spend_key, state3 };
|
let state = AliceState::BtcRefunded { spend_key, state3 };
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -409,7 +413,7 @@ pub async fn run_until(
|
|||||||
|
|
||||||
let state = AliceState::XmrRefunded;
|
let state = AliceState::XmrRefunded;
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
@ -439,9 +443,9 @@ pub async fn run_until(
|
|||||||
Either::Left(_) => {
|
Either::Left(_) => {
|
||||||
let state = AliceState::BtcPunished;
|
let state = AliceState::BtcPunished;
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -463,9 +467,9 @@ pub async fn run_until(
|
|||||||
)?;
|
)?;
|
||||||
let state = AliceState::BtcRefunded { spend_key, state3 };
|
let state = AliceState::BtcRefunded { spend_key, state3 };
|
||||||
let db_state = (&state).into();
|
let db_state = (&state).into();
|
||||||
db.insert_latest_state(swap_id, Swap::Alice(db_state))
|
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||||
.await?;
|
.await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
|
@ -8,7 +8,9 @@ use libp2p::{
|
|||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bitcoin,
|
||||||
bitcoin::EncryptedSignature,
|
bitcoin::EncryptedSignature,
|
||||||
|
monero,
|
||||||
network::{
|
network::{
|
||||||
peer_tracker::{self, PeerTracker},
|
peer_tracker::{self, PeerTracker},
|
||||||
transport::SwapTransport,
|
transport::SwapTransport,
|
||||||
@ -26,8 +28,11 @@ pub use self::{
|
|||||||
message2::Message2,
|
message2::Message2,
|
||||||
message3::Message3,
|
message3::Message3,
|
||||||
state::*,
|
state::*,
|
||||||
swap::{run_until, swap},
|
swap::{run, run_until},
|
||||||
};
|
};
|
||||||
|
use crate::database::Database;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod amounts;
|
mod amounts;
|
||||||
pub mod event_loop;
|
pub mod event_loop;
|
||||||
@ -38,6 +43,15 @@ mod message3;
|
|||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod swap;
|
pub mod swap;
|
||||||
|
|
||||||
|
pub struct Swap {
|
||||||
|
pub state: BobState,
|
||||||
|
pub event_loop_handle: bob::EventLoopHandle,
|
||||||
|
pub db: Database,
|
||||||
|
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
|
pub monero_wallet: Arc<monero::Wallet>,
|
||||||
|
pub swap_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
pub type Swarm = libp2p::Swarm<Behaviour>;
|
pub type Swarm = libp2p::Swarm<Behaviour>;
|
||||||
|
|
||||||
pub fn new_swarm(transport: SwapTransport, behaviour: Behaviour) -> Result<Swarm> {
|
pub fn new_swarm(transport: SwapTransport, behaviour: Behaviour) -> Result<Swarm> {
|
||||||
|
@ -13,33 +13,7 @@ use crate::{
|
|||||||
protocol::bob::{self, event_loop::EventLoopHandle, state::*},
|
protocol::bob::{self, event_loop::EventLoopHandle, state::*},
|
||||||
ExpiredTimelocks, SwapAmounts,
|
ExpiredTimelocks, SwapAmounts,
|
||||||
};
|
};
|
||||||
|
use ecdsa_fun::fun::rand_core::OsRng;
|
||||||
// TODO(Franck): Make this a method on a struct
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub async fn swap<R>(
|
|
||||||
state: BobState,
|
|
||||||
event_loop_handle: EventLoopHandle,
|
|
||||||
db: Database,
|
|
||||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
|
||||||
rng: R,
|
|
||||||
swap_id: Uuid,
|
|
||||||
) -> Result<BobState>
|
|
||||||
where
|
|
||||||
R: RngCore + CryptoRng + Send,
|
|
||||||
{
|
|
||||||
run_until(
|
|
||||||
state,
|
|
||||||
is_complete,
|
|
||||||
event_loop_handle,
|
|
||||||
db,
|
|
||||||
bitcoin_wallet,
|
|
||||||
monero_wallet,
|
|
||||||
rng,
|
|
||||||
swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_complete(state: &BobState) -> bool {
|
pub fn is_complete(state: &BobState) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
@ -63,10 +37,32 @@ pub fn is_encsig_sent(state: &BobState) -> bool {
|
|||||||
matches!(state, BobState::EncSigSent(..))
|
matches!(state, BobState::EncSigSent(..))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn run(swap: bob::Swap) -> Result<BobState> {
|
||||||
|
run_until(swap, is_complete).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_until(
|
||||||
|
swap: bob::Swap,
|
||||||
|
is_target_state: fn(&BobState) -> bool,
|
||||||
|
) -> Result<BobState> {
|
||||||
|
do_run_until(
|
||||||
|
swap.state,
|
||||||
|
is_target_state,
|
||||||
|
swap.event_loop_handle,
|
||||||
|
swap.db,
|
||||||
|
swap.bitcoin_wallet,
|
||||||
|
swap.monero_wallet,
|
||||||
|
OsRng,
|
||||||
|
swap.swap_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
// 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]
|
||||||
pub async fn run_until<R>(
|
async fn do_run_until<R>(
|
||||||
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,
|
||||||
@ -99,7 +95,7 @@ where
|
|||||||
let state = BobState::Negotiated(state2);
|
let state = BobState::Negotiated(state2);
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -120,7 +116,7 @@ where
|
|||||||
let state = BobState::BtcLocked(state3);
|
let state = BobState::BtcLocked(state3);
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -185,7 +181,7 @@ where
|
|||||||
};
|
};
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -226,7 +222,7 @@ where
|
|||||||
};
|
};
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -261,7 +257,7 @@ where
|
|||||||
|
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -282,7 +278,7 @@ where
|
|||||||
};
|
};
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -307,7 +303,7 @@ where
|
|||||||
db.insert_latest_state(swap_id, Swap::Bob(state.clone().into()))
|
db.insert_latest_state(swap_id, Swap::Bob(state.clone().into()))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
@ -336,7 +332,7 @@ where
|
|||||||
|
|
||||||
let db_state = state.clone().into();
|
let db_state = state.clone().into();
|
||||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||||
run_until(
|
do_run_until(
|
||||||
state,
|
state,
|
||||||
is_target_state,
|
is_target_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use rand::rngs::OsRng;
|
|
||||||
use swap::protocol::{alice, bob};
|
use swap::protocol::{alice, bob};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
|
|
||||||
@ -8,33 +7,17 @@ pub mod testutils;
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn happy_path() {
|
async fn happy_path() {
|
||||||
testutils::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let alice_swap = alice::swap(
|
let alice = alice::run(alice_swap);
|
||||||
alice.state,
|
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
);
|
|
||||||
|
|
||||||
let bob_swap = bob::swap(
|
let bob = bob::run(bob_swap);
|
||||||
bob.state,
|
let (alice_state, bob_state) = join!(alice, bob);
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
);
|
|
||||||
let (alice_state, bob_state) = join!(alice_swap, bob_swap);
|
|
||||||
|
|
||||||
alice_harness.assert_redeemed(alice_state.unwrap()).await;
|
test.assert_alice_redeemed(alice_state.unwrap()).await;
|
||||||
bob_harness.assert_redeemed(bob_state.unwrap()).await;
|
test.assert_bob_redeemed(bob_state.unwrap()).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,30 @@
|
|||||||
use rand::rngs::OsRng;
|
|
||||||
use swap::protocol::{alice, alice::AliceState, bob};
|
use swap::protocol::{alice, alice::AliceState, bob};
|
||||||
|
|
||||||
pub mod testutils;
|
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::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let bob_swap = bob::swap(
|
let bob = bob::run(bob_swap);
|
||||||
bob.state,
|
let bob_handle = tokio::spawn(bob);
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
);
|
|
||||||
let bob_swap_handle = tokio::spawn(bob_swap);
|
|
||||||
|
|
||||||
let alice_state = alice::run_until(
|
let alice_state = alice::run_until(alice_swap, alice::swap::is_encsig_learned)
|
||||||
alice.state,
|
|
||||||
alice::swap::is_encsig_learned,
|
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(alice_state, AliceState::EncSigLearned {..}));
|
assert!(matches!(alice_state, AliceState::EncSigLearned {..}));
|
||||||
|
|
||||||
let alice = alice_harness.recover_alice_from_db().await;
|
let alice_swap = test.recover_alice_from_db().await;
|
||||||
assert!(matches!(alice.state, AliceState::EncSigLearned {..}));
|
assert!(matches!(alice_swap.state, AliceState::EncSigLearned {..}));
|
||||||
|
|
||||||
let alice_state = alice::swap(
|
let alice_state = alice::run(alice_swap).await.unwrap();
|
||||||
alice.state,
|
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
alice_harness.assert_redeemed(alice_state).await;
|
test.assert_alice_redeemed(alice_state).await;
|
||||||
|
|
||||||
let bob_state = bob_swap_handle.await.unwrap();
|
let bob_state = bob_handle.await.unwrap();
|
||||||
bob_harness.assert_redeemed(bob_state.unwrap()).await
|
test.assert_bob_redeemed(bob_state.unwrap()).await
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,31 @@
|
|||||||
use rand::rngs::OsRng;
|
|
||||||
use swap::protocol::{alice, bob, bob::BobState};
|
use swap::protocol::{alice, bob, bob::BobState};
|
||||||
|
|
||||||
pub mod testutils;
|
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::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let alice_swap = alice::swap(
|
let alice = alice::run(alice_swap);
|
||||||
alice.state,
|
let alice_handle = tokio::spawn(alice);
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
);
|
|
||||||
let alice_swap_handle = tokio::spawn(alice_swap);
|
|
||||||
|
|
||||||
let bob_state = bob::run_until(
|
let bob_state = bob::run_until(bob_swap, bob::swap::is_encsig_sent)
|
||||||
bob.state,
|
|
||||||
bob::swap::is_encsig_sent,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(bob_state, BobState::EncSigSent {..}));
|
assert!(matches!(bob_state, BobState::EncSigSent {..}));
|
||||||
|
|
||||||
let bob = bob_harness.recover_bob_from_db().await;
|
let bob_swap = test.recover_bob_from_db().await;
|
||||||
assert!(matches!(bob.state, BobState::EncSigSent {..}));
|
assert!(matches!(bob_swap.state, BobState::EncSigSent {..}));
|
||||||
|
|
||||||
let bob_state = bob::swap(
|
let bob_state = bob::run(bob_swap).await.unwrap();
|
||||||
bob.state,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
bob_harness.assert_redeemed(bob_state).await;
|
test.assert_bob_redeemed(bob_state).await;
|
||||||
|
|
||||||
let alice_state = alice_swap_handle.await.unwrap();
|
let alice_state = alice_handle.await.unwrap();
|
||||||
alice_harness.assert_redeemed(alice_state.unwrap()).await;
|
test.assert_alice_redeemed(alice_state.unwrap()).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,32 @@
|
|||||||
use rand::rngs::OsRng;
|
use swap::protocol::{
|
||||||
use swap::protocol::{alice, bob, bob::BobState};
|
alice, bob,
|
||||||
|
bob::{swap::is_xmr_locked, BobState},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod testutils;
|
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::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let alice_swap = alice::swap(
|
let alice_handle = alice::run(alice_swap);
|
||||||
alice.state,
|
let alice_swap_handle = tokio::spawn(alice_handle);
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
);
|
|
||||||
let alice_swap_handle = tokio::spawn(alice_swap);
|
|
||||||
|
|
||||||
let bob_state = bob::run_until(
|
let bob_state = bob::run_until(bob_swap, is_xmr_locked).await.unwrap();
|
||||||
bob.state,
|
|
||||||
bob::swap::is_xmr_locked,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(matches!(bob_state, BobState::XmrLocked {..}));
|
assert!(matches!(bob_state, BobState::XmrLocked {..}));
|
||||||
|
|
||||||
let bob = bob_harness.recover_bob_from_db().await;
|
let bob_swap = test.recover_bob_from_db().await;
|
||||||
assert!(matches!(bob.state, BobState::XmrLocked {..}));
|
assert!(matches!(bob_swap.state, BobState::XmrLocked {..}));
|
||||||
|
|
||||||
let bob_state = bob::swap(
|
let bob_state = bob::run(bob_swap).await.unwrap();
|
||||||
bob.state,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
bob_harness.assert_redeemed(bob_state).await;
|
test.assert_bob_redeemed(bob_state).await;
|
||||||
|
|
||||||
let alice_state = alice_swap_handle.await.unwrap();
|
let alice_state = alice_swap_handle.await.unwrap();
|
||||||
alice_harness.assert_redeemed(alice_state.unwrap()).await;
|
test.assert_alice_redeemed(alice_state.unwrap()).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use rand::rngs::OsRng;
|
use swap::protocol::{
|
||||||
use swap::protocol::{alice, bob, bob::BobState};
|
alice, bob,
|
||||||
|
bob::{swap::is_btc_locked, BobState},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod testutils;
|
pub mod testutils;
|
||||||
|
|
||||||
@ -7,57 +9,28 @@ 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::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let alice_swap = alice::swap(
|
let alice = alice::run(alice_swap);
|
||||||
alice.state,
|
let alice_handle = tokio::spawn(alice);
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
);
|
|
||||||
let alice_swap_handle = tokio::spawn(alice_swap);
|
|
||||||
|
|
||||||
let bob_state = bob::run_until(
|
let bob_state = bob::run_until(bob_swap, is_btc_locked).await.unwrap();
|
||||||
bob.state,
|
|
||||||
bob::swap::is_btc_locked,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(matches!(bob_state, BobState::BtcLocked {..}));
|
assert!(matches!(bob_state, BobState::BtcLocked {..}));
|
||||||
|
|
||||||
let alice_state = alice_swap_handle.await.unwrap();
|
let alice_state = alice_handle.await.unwrap();
|
||||||
alice_harness.assert_punished(alice_state.unwrap()).await;
|
test.assert_alice_punished(alice_state.unwrap()).await;
|
||||||
|
|
||||||
// Restart Bob after Alice punished to ensure Bob transitions to
|
// Restart Bob after Alice punished to ensure Bob transitions to
|
||||||
// punished and does not run indefinitely
|
// punished and does not run indefinitely
|
||||||
let bob = bob_harness.recover_bob_from_db().await;
|
let bob_swap = test.recover_bob_from_db().await;
|
||||||
assert!(matches!(bob.state, BobState::BtcLocked {..}));
|
assert!(matches!(bob_swap.state, BobState::BtcLocked {..}));
|
||||||
|
|
||||||
let bob_state = bob::swap(
|
let bob_state = bob::run(bob_swap).await.unwrap();
|
||||||
bob.state,
|
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
bob_harness.assert_punished(bob_state).await;
|
test.assert_bob_punished(bob_state).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use rand::rngs::OsRng;
|
|
||||||
use swap::protocol::{alice, alice::AliceState, bob};
|
use swap::protocol::{alice, alice::AliceState, bob};
|
||||||
|
|
||||||
pub mod testutils;
|
pub mod testutils;
|
||||||
@ -7,60 +6,33 @@ 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::test(|alice_harness, bob_harness| async move {
|
testutils::init(|test| async move {
|
||||||
let alice = alice_harness.new_alice().await;
|
let alice_swap = test.new_swap_as_alice().await;
|
||||||
let bob = bob_harness.new_bob().await;
|
let bob_swap = test.new_swap_as_bob().await;
|
||||||
|
|
||||||
let bob_swap = bob::swap(
|
let bob = bob::run(bob_swap);
|
||||||
bob.state,
|
let bob_handle = tokio::spawn(bob);
|
||||||
bob.event_loop_handle,
|
|
||||||
bob.db,
|
|
||||||
bob.bitcoin_wallet.clone(),
|
|
||||||
bob.monero_wallet.clone(),
|
|
||||||
OsRng,
|
|
||||||
bob.swap_id,
|
|
||||||
);
|
|
||||||
let bob_swap_handle = tokio::spawn(bob_swap);
|
|
||||||
|
|
||||||
let alice_state = alice::run_until(
|
let alice_state = alice::run_until(alice_swap, alice::swap::is_xmr_locked)
|
||||||
alice.state,
|
|
||||||
alice::swap::is_xmr_locked,
|
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(alice_state, AliceState::XmrLocked {..}));
|
assert!(matches!(alice_state, AliceState::XmrLocked {..}));
|
||||||
|
|
||||||
// Alice does not act, Bob refunds
|
// Alice does not act, Bob refunds
|
||||||
let bob_state = bob_swap_handle.await.unwrap();
|
let bob_state = bob_handle.await.unwrap();
|
||||||
|
|
||||||
// Once bob has finished Alice is restarted and refunds as well
|
// Once bob has finished Alice is restarted and refunds as well
|
||||||
let alice = alice_harness.recover_alice_from_db().await;
|
let alice_swap = test.recover_alice_from_db().await;
|
||||||
assert!(matches!(alice.state, AliceState::XmrLocked {..}));
|
assert!(matches!(alice_swap.state, AliceState::XmrLocked {..}));
|
||||||
|
|
||||||
let alice_state = alice::swap(
|
let alice_state = alice::run(alice_swap).await.unwrap();
|
||||||
alice.state,
|
|
||||||
alice.event_loop_handle,
|
|
||||||
alice.bitcoin_wallet.clone(),
|
|
||||||
alice.monero_wallet.clone(),
|
|
||||||
alice.config,
|
|
||||||
alice.swap_id,
|
|
||||||
alice.db,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// TODO: The test passes like this, but the assertion should be done after Bob
|
// TODO: The test passes like this, but the assertion should be done after Bob
|
||||||
// refunded, not at the end because this can cause side-effects!
|
// refunded, not at the end because this can cause side-effects!
|
||||||
// We have to properly wait for the refund tx's finality inside the assertion,
|
// We have to properly wait for the refund tx's finality inside the assertion,
|
||||||
// which requires storing the refund_tx_id in the the state!
|
// which requires storing the refund_tx_id in the the state!
|
||||||
bob_harness.assert_refunded(bob_state.unwrap()).await;
|
test.assert_bob_refunded(bob_state.unwrap()).await;
|
||||||
alice_harness.assert_refunded(alice_state).await;
|
test.assert_alice_refunded(alice_state).await;
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,285 @@ use tracing_core::dispatcher::DefaultGuard;
|
|||||||
use tracing_log::LogTracer;
|
use tracing_log::LogTracer;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub async fn test<T, F>(testfn: T)
|
pub struct Test {
|
||||||
|
swap_amounts: SwapAmounts,
|
||||||
|
|
||||||
|
alice_swap_factory: AliceSwapFactory,
|
||||||
|
bob_swap_factory: BobSwapFactory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
pub async fn new_swap_as_alice(&self) -> alice::Swap {
|
||||||
|
let (swap, mut event_loop) = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.new_swap_as_alice(self.swap_amounts)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
|
|
||||||
|
swap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_swap_as_bob(&self) -> bob::Swap {
|
||||||
|
let (swap, event_loop) = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.new_swap_as_bob(self.swap_amounts)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
|
|
||||||
|
swap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recover_alice_from_db(&self) -> alice::Swap {
|
||||||
|
let (swap, mut event_loop) = self.alice_swap_factory.recover_alice_from_db().await;
|
||||||
|
|
||||||
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
|
|
||||||
|
swap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recover_bob_from_db(&self) -> bob::Swap {
|
||||||
|
let (swap, event_loop) = self.bob_swap_factory.recover_bob_from_db().await;
|
||||||
|
|
||||||
|
tokio::spawn(async move { event_loop.run().await });
|
||||||
|
|
||||||
|
swap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert_alice_redeemed(&self, state: AliceState) {
|
||||||
|
assert!(matches!(state, AliceState::BtcRedeemed));
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
btc_balance_after_swap,
|
||||||
|
self.alice_swap_factory.starting_balances.btc + self.swap_amounts.btc
|
||||||
|
- bitcoin::Amount::from_sat(bitcoin::TX_FEE)
|
||||||
|
);
|
||||||
|
|
||||||
|
let xmr_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.get_balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
xmr_balance_after_swap
|
||||||
|
<= self.alice_swap_factory.starting_balances.xmr - self.swap_amounts.xmr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert_alice_refunded(&self, state: AliceState) {
|
||||||
|
assert!(matches!(state, AliceState::XmrRefunded));
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.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
|
||||||
|
self.alice_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.inner
|
||||||
|
.refresh()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let xmr_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.get_balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert_alice_punished(&self, state: AliceState) {
|
||||||
|
assert!(matches!(state, AliceState::BtcPunished));
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
btc_balance_after_swap,
|
||||||
|
self.alice_swap_factory.starting_balances.btc + self.swap_amounts.btc
|
||||||
|
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE)
|
||||||
|
);
|
||||||
|
|
||||||
|
let xmr_balance_after_swap = self
|
||||||
|
.alice_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.get_balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
xmr_balance_after_swap
|
||||||
|
<= self.alice_swap_factory.starting_balances.xmr - self.swap_amounts.xmr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert_bob_redeemed(&self, state: BobState) {
|
||||||
|
let lock_tx_id = if let BobState::XmrRedeemed { tx_lock_id } = state {
|
||||||
|
tx_lock_id
|
||||||
|
} else {
|
||||||
|
panic!("Bob in unexpected state");
|
||||||
|
};
|
||||||
|
|
||||||
|
let lock_tx_bitcoin_fee = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.transaction_fee(lock_tx_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
btc_balance_after_swap,
|
||||||
|
self.bob_swap_factory.starting_balances.btc
|
||||||
|
- self.swap_amounts.btc
|
||||||
|
- lock_tx_bitcoin_fee
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure that Bob's balance is refreshed as we use a newly created wallet
|
||||||
|
self.bob_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.inner
|
||||||
|
.refresh()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let xmr_balance_after_swap = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.get_balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
xmr_balance_after_swap,
|
||||||
|
self.bob_swap_factory.starting_balances.xmr + self.swap_amounts.xmr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert_bob_refunded(&self, state: BobState) {
|
||||||
|
let lock_tx_id = if let BobState::BtcRefunded(state4) = state {
|
||||||
|
state4.tx_lock_id()
|
||||||
|
} else {
|
||||||
|
panic!("Bob in unexpected state");
|
||||||
|
};
|
||||||
|
let lock_tx_bitcoin_fee = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.transaction_fee(lock_tx_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let alice_submitted_cancel = btc_balance_after_swap
|
||||||
|
== self.bob_swap_factory.starting_balances.btc
|
||||||
|
- lock_tx_bitcoin_fee
|
||||||
|
- bitcoin::Amount::from_sat(bitcoin::TX_FEE);
|
||||||
|
|
||||||
|
let bob_submitted_cancel = btc_balance_after_swap
|
||||||
|
== self.bob_swap_factory.starting_balances.btc
|
||||||
|
- lock_tx_bitcoin_fee
|
||||||
|
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE);
|
||||||
|
|
||||||
|
// The cancel tx can be submitted by both Alice and Bob.
|
||||||
|
// 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_swap_factory
|
||||||
|
.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) {
|
||||||
|
let lock_tx_id = if let BobState::BtcPunished { tx_lock_id } = state {
|
||||||
|
tx_lock_id
|
||||||
|
} else {
|
||||||
|
panic!("Bob in unexpected state");
|
||||||
|
};
|
||||||
|
|
||||||
|
let lock_tx_bitcoin_fee = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.transaction_fee(lock_tx_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let btc_balance_after_swap = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
btc_balance_after_swap,
|
||||||
|
self.bob_swap_factory.starting_balances.btc
|
||||||
|
- self.swap_amounts.btc
|
||||||
|
- lock_tx_bitcoin_fee
|
||||||
|
);
|
||||||
|
|
||||||
|
let xmr_balance_after_swap = self
|
||||||
|
.bob_swap_factory
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.get_balance()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
xmr_balance_after_swap,
|
||||||
|
self.bob_swap_factory.starting_balances.xmr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init<T, F>(testfn: T)
|
||||||
where
|
where
|
||||||
T: Fn(AliceHarness, BobHarness) -> F,
|
T: Fn(Test) -> F,
|
||||||
F: Future<Output = ()>,
|
F: Future<Output = ()>,
|
||||||
{
|
{
|
||||||
let cli = Cli::default();
|
let cli = Cli::default();
|
||||||
@ -44,9 +320,8 @@ where
|
|||||||
xmr: swap_amounts.xmr * 10,
|
xmr: swap_amounts.xmr * 10,
|
||||||
btc: bitcoin::Amount::ZERO,
|
btc: bitcoin::Amount::ZERO,
|
||||||
};
|
};
|
||||||
let alice_harness = AliceHarness::new(
|
let alice_swap_factory = AliceSwapFactory::new(
|
||||||
config,
|
config,
|
||||||
swap_amounts,
|
|
||||||
Uuid::new_v4(),
|
Uuid::new_v4(),
|
||||||
&monero,
|
&monero,
|
||||||
&containers.bitcoind,
|
&containers.bitcoind,
|
||||||
@ -59,32 +334,27 @@ where
|
|||||||
btc: swap_amounts.btc * 10,
|
btc: swap_amounts.btc * 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bob_harness = BobHarness::new(
|
let bob_swap_factory = BobSwapFactory::new(
|
||||||
config,
|
config,
|
||||||
swap_amounts,
|
|
||||||
Uuid::new_v4(),
|
Uuid::new_v4(),
|
||||||
&monero,
|
&monero,
|
||||||
&containers.bitcoind,
|
&containers.bitcoind,
|
||||||
bob_starting_balances,
|
bob_starting_balances,
|
||||||
alice_harness.listen_address(),
|
alice_swap_factory.listen_address(),
|
||||||
alice_harness.peer_id(),
|
alice_swap_factory.peer_id(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
testfn(alice_harness, bob_harness).await
|
let test = Test {
|
||||||
|
swap_amounts,
|
||||||
|
alice_swap_factory,
|
||||||
|
bob_swap_factory,
|
||||||
|
};
|
||||||
|
|
||||||
|
testfn(test).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Alice {
|
pub struct AliceSwapFactory {
|
||||||
pub state: AliceState,
|
|
||||||
pub event_loop_handle: alice::EventLoopHandle,
|
|
||||||
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
|
||||||
pub monero_wallet: Arc<monero::Wallet>,
|
|
||||||
pub config: Config,
|
|
||||||
pub swap_id: Uuid,
|
|
||||||
pub db: Database,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AliceHarness {
|
|
||||||
listen_address: Multiaddr,
|
listen_address: Multiaddr,
|
||||||
peer_id: PeerId,
|
peer_id: PeerId,
|
||||||
|
|
||||||
@ -92,17 +362,15 @@ pub struct AliceHarness {
|
|||||||
db_path: PathBuf,
|
db_path: PathBuf,
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
|
|
||||||
swap_amounts: SwapAmounts,
|
|
||||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
config: Config,
|
config: Config,
|
||||||
starting_balances: StartingBalances,
|
starting_balances: StartingBalances,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AliceHarness {
|
impl AliceSwapFactory {
|
||||||
async fn new(
|
async fn new(
|
||||||
config: Config,
|
config: Config,
|
||||||
swap_amounts: SwapAmounts,
|
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
monero: &Monero,
|
monero: &Monero,
|
||||||
bitcoind: &Bitcoind<'_>,
|
bitcoind: &Bitcoind<'_>,
|
||||||
@ -132,7 +400,6 @@ impl AliceHarness {
|
|||||||
listen_address,
|
listen_address,
|
||||||
peer_id,
|
peer_id,
|
||||||
swap_id,
|
swap_id,
|
||||||
swap_amounts,
|
|
||||||
bitcoin_wallet,
|
bitcoin_wallet,
|
||||||
monero_wallet,
|
monero_wallet,
|
||||||
config,
|
config,
|
||||||
@ -140,23 +407,24 @@ impl AliceHarness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_alice(&self) -> Alice {
|
pub async fn new_swap_as_alice(
|
||||||
|
&self,
|
||||||
|
swap_amounts: SwapAmounts,
|
||||||
|
) -> (alice::Swap, alice::EventLoop) {
|
||||||
let initial_state = init_alice_state(
|
let initial_state = init_alice_state(
|
||||||
self.swap_amounts.btc,
|
swap_amounts.btc,
|
||||||
self.swap_amounts.xmr,
|
swap_amounts.xmr,
|
||||||
self.bitcoin_wallet.clone(),
|
self.bitcoin_wallet.clone(),
|
||||||
self.config,
|
self.config,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (mut event_loop, event_loop_handle) =
|
let (event_loop, event_loop_handle) =
|
||||||
init_alice_event_loop(self.listen_address.clone(), self.seed);
|
init_alice_event_loop(self.listen_address.clone(), self.seed);
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
|
||||||
|
|
||||||
let db = Database::open(self.db_path.as_path()).unwrap();
|
let db = Database::open(self.db_path.as_path()).unwrap();
|
||||||
|
(
|
||||||
Alice {
|
alice::Swap {
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
monero_wallet: self.monero_wallet.clone(),
|
monero_wallet: self.monero_wallet.clone(),
|
||||||
@ -164,15 +432,12 @@ impl AliceHarness {
|
|||||||
db,
|
db,
|
||||||
state: initial_state,
|
state: initial_state,
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
}
|
},
|
||||||
|
event_loop,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recover_alice_from_db(&self) -> Alice {
|
pub async fn recover_alice_from_db(&self) -> (alice::Swap, alice::EventLoop) {
|
||||||
// TODO: "simulated restart" issues:
|
|
||||||
// - create new wallets instead of reusing (hard because of container
|
|
||||||
// lifetimes)
|
|
||||||
// - consider aborting the old event loop (currently just keeps running)
|
|
||||||
|
|
||||||
// reopen the existing database
|
// reopen the existing database
|
||||||
let db = Database::open(self.db_path.clone().as_path()).unwrap();
|
let db = Database::open(self.db_path.clone().as_path()).unwrap();
|
||||||
|
|
||||||
@ -183,12 +448,11 @@ impl AliceHarness {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut event_loop, event_loop_handle) =
|
let (event_loop, event_loop_handle) =
|
||||||
init_alice_event_loop(self.listen_address.clone(), self.seed);
|
init_alice_event_loop(self.listen_address.clone(), self.seed);
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
(
|
||||||
|
alice::Swap {
|
||||||
Alice {
|
|
||||||
state: resume_state,
|
state: resume_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
@ -196,47 +460,9 @@ impl AliceHarness {
|
|||||||
config: self.config,
|
config: self.config,
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
db,
|
db,
|
||||||
}
|
},
|
||||||
}
|
event_loop,
|
||||||
|
)
|
||||||
pub async fn assert_redeemed(&self, state: AliceState) {
|
|
||||||
assert!(matches!(state, AliceState::BtcRedeemed));
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
btc_balance_after_swap,
|
|
||||||
self.starting_balances.btc + self.swap_amounts.btc
|
|
||||||
- bitcoin::Amount::from_sat(bitcoin::TX_FEE)
|
|
||||||
);
|
|
||||||
|
|
||||||
let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert!(xmr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn assert_refunded(&self, state: AliceState) {
|
|
||||||
assert!(matches!(state, AliceState::XmrRefunded));
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
assert_eq!(btc_balance_after_swap, self.starting_balances.btc);
|
|
||||||
|
|
||||||
// Ensure that Alice's balance is refreshed as we use a newly created wallet
|
|
||||||
self.monero_wallet.as_ref().inner.refresh().await.unwrap();
|
|
||||||
let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert_eq!(xmr_balance_after_swap, self.swap_amounts.xmr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn assert_punished(&self, state: AliceState) {
|
|
||||||
assert!(matches!(state, AliceState::BtcPunished));
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
btc_balance_after_swap,
|
|
||||||
self.starting_balances.btc + self.swap_amounts.btc
|
|
||||||
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE)
|
|
||||||
);
|
|
||||||
|
|
||||||
let xnr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert!(xnr_balance_after_swap <= self.starting_balances.xmr - self.swap_amounts.xmr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_id(&self) -> PeerId {
|
pub fn peer_id(&self) -> PeerId {
|
||||||
@ -248,20 +474,10 @@ impl AliceHarness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bob {
|
pub struct BobSwapFactory {
|
||||||
pub state: BobState,
|
|
||||||
pub event_loop_handle: bob::EventLoopHandle,
|
|
||||||
pub db: Database,
|
|
||||||
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
|
|
||||||
pub monero_wallet: Arc<monero::Wallet>,
|
|
||||||
pub swap_id: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BobHarness {
|
|
||||||
db_path: PathBuf,
|
db_path: PathBuf,
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
|
|
||||||
swap_amounts: SwapAmounts,
|
|
||||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
config: Config,
|
config: Config,
|
||||||
@ -271,11 +487,10 @@ pub struct BobHarness {
|
|||||||
alice_connect_peer_id: PeerId,
|
alice_connect_peer_id: PeerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BobHarness {
|
impl BobSwapFactory {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn new(
|
async fn new(
|
||||||
config: Config,
|
config: Config,
|
||||||
swap_amounts: SwapAmounts,
|
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
monero: &Monero,
|
monero: &Monero,
|
||||||
bitcoind: &Bitcoind<'_>,
|
bitcoind: &Bitcoind<'_>,
|
||||||
@ -291,7 +506,6 @@ impl BobHarness {
|
|||||||
Self {
|
Self {
|
||||||
db_path,
|
db_path,
|
||||||
swap_id,
|
swap_id,
|
||||||
swap_amounts,
|
|
||||||
bitcoin_wallet,
|
bitcoin_wallet,
|
||||||
monero_wallet,
|
monero_wallet,
|
||||||
config,
|
config,
|
||||||
@ -301,10 +515,10 @@ impl BobHarness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_bob(&self) -> Bob {
|
pub async fn new_swap_as_bob(&self, swap_amounts: SwapAmounts) -> (bob::Swap, bob::EventLoop) {
|
||||||
let initial_state = init_bob_state(
|
let initial_state = init_bob_state(
|
||||||
self.swap_amounts.btc,
|
swap_amounts.btc,
|
||||||
self.swap_amounts.xmr,
|
swap_amounts.xmr,
|
||||||
self.bitcoin_wallet.clone(),
|
self.bitcoin_wallet.clone(),
|
||||||
self.config,
|
self.config,
|
||||||
)
|
)
|
||||||
@ -315,26 +529,22 @@ impl BobHarness {
|
|||||||
self.alice_connect_address.clone(),
|
self.alice_connect_address.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
|
||||||
|
|
||||||
let db = Database::open(self.db_path.as_path()).unwrap();
|
let db = Database::open(self.db_path.as_path()).unwrap();
|
||||||
|
|
||||||
Bob {
|
(
|
||||||
|
bob::Swap {
|
||||||
state: initial_state,
|
state: initial_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
db,
|
db,
|
||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
monero_wallet: self.monero_wallet.clone(),
|
monero_wallet: self.monero_wallet.clone(),
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
}
|
},
|
||||||
|
event_loop,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recover_bob_from_db(&self) -> Bob {
|
pub async fn recover_bob_from_db(&self) -> (bob::Swap, bob::EventLoop) {
|
||||||
// TODO: "simulated restart" issues:
|
|
||||||
// - create new wallets instead of reusing (hard because of container
|
|
||||||
// lifetimes)
|
|
||||||
// - consider aborting the old event loop (currently just keeps running)
|
|
||||||
|
|
||||||
// reopen the existing database
|
// reopen the existing database
|
||||||
let db = Database::open(self.db_path.clone().as_path()).unwrap();
|
let db = Database::open(self.db_path.clone().as_path()).unwrap();
|
||||||
|
|
||||||
@ -350,99 +560,17 @@ impl BobHarness {
|
|||||||
self.alice_connect_address.clone(),
|
self.alice_connect_address.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
tokio::spawn(async move { event_loop.run().await });
|
(
|
||||||
|
bob::Swap {
|
||||||
Bob {
|
|
||||||
state: resume_state,
|
state: resume_state,
|
||||||
event_loop_handle,
|
event_loop_handle,
|
||||||
db,
|
db,
|
||||||
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
bitcoin_wallet: self.bitcoin_wallet.clone(),
|
||||||
monero_wallet: self.monero_wallet.clone(),
|
monero_wallet: self.monero_wallet.clone(),
|
||||||
swap_id: self.swap_id,
|
swap_id: self.swap_id,
|
||||||
}
|
},
|
||||||
}
|
event_loop,
|
||||||
|
)
|
||||||
pub async fn assert_redeemed(&self, state: BobState) {
|
|
||||||
let lock_tx_id = if let BobState::XmrRedeemed { tx_lock_id } = state {
|
|
||||||
tx_lock_id
|
|
||||||
} else {
|
|
||||||
panic!("Bob in unexpected state");
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock_tx_bitcoin_fee = self
|
|
||||||
.bitcoin_wallet
|
|
||||||
.transaction_fee(lock_tx_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
btc_balance_after_swap,
|
|
||||||
self.starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure that Bob's balance is refreshed as we use a newly created wallet
|
|
||||||
self.monero_wallet.as_ref().inner.refresh().await.unwrap();
|
|
||||||
let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
xmr_balance_after_swap,
|
|
||||||
self.starting_balances.xmr + self.swap_amounts.xmr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn assert_refunded(&self, state: BobState) {
|
|
||||||
let lock_tx_id = if let BobState::BtcRefunded(state4) = state {
|
|
||||||
state4.tx_lock_id()
|
|
||||||
} else {
|
|
||||||
panic!("Bob in unexpected state");
|
|
||||||
};
|
|
||||||
let lock_tx_bitcoin_fee = self
|
|
||||||
.bitcoin_wallet
|
|
||||||
.transaction_fee(lock_tx_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
|
|
||||||
let alice_submitted_cancel = btc_balance_after_swap
|
|
||||||
== self.starting_balances.btc
|
|
||||||
- lock_tx_bitcoin_fee
|
|
||||||
- bitcoin::Amount::from_sat(bitcoin::TX_FEE);
|
|
||||||
|
|
||||||
let bob_submitted_cancel = btc_balance_after_swap
|
|
||||||
== self.starting_balances.btc
|
|
||||||
- lock_tx_bitcoin_fee
|
|
||||||
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE);
|
|
||||||
|
|
||||||
// The cancel tx can be submitted by both Alice and Bob.
|
|
||||||
// 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.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert_eq!(xmr_balance_after_swap, self.starting_balances.xmr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn assert_punished(&self, state: BobState) {
|
|
||||||
let lock_tx_id = if let BobState::BtcPunished { tx_lock_id } = state {
|
|
||||||
tx_lock_id
|
|
||||||
} else {
|
|
||||||
panic!("Bob in unexpected state");
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock_tx_bitcoin_fee = self
|
|
||||||
.bitcoin_wallet
|
|
||||||
.transaction_fee(lock_tx_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let btc_balance_after_swap = self.bitcoin_wallet.as_ref().balance().await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
btc_balance_after_swap,
|
|
||||||
self.starting_balances.btc - self.swap_amounts.btc - lock_tx_bitcoin_fee
|
|
||||||
);
|
|
||||||
|
|
||||||
let xmr_balance_after_swap = self.monero_wallet.as_ref().get_balance().await.unwrap();
|
|
||||||
assert_eq!(xmr_balance_after_swap, self.starting_balances.xmr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user