Execute Alice's on-chain protocol after handshake

Co-authored-by: Tobin C. Harding <tobin@coblox.tech>
This commit is contained in:
Lucas Soriano del Pino 2020-10-27 12:11:03 +11:00
parent dbd7f2b0c9
commit 4ee82a5a2a
13 changed files with 402 additions and 65 deletions

View File

@ -12,13 +12,15 @@ atty = "0.2"
backoff = { version = "0.2", features = ["tokio"] }
base64 = "0.12"
bitcoin = { version = "0.23", features = ["rand", "use-serde"] } # TODO: Upgrade other crates in this repo to use this version.
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "d402b36d3d6406150e3bfb71492ff4a0a7cb290e" }
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "7ff30a559ab57cc3aa71189e71433ef6b2a6c3a2" }
derivative = "2"
futures = { version = "0.3", default-features = false }
genawaiter = "0.99.1"
libp2p = { version = "0.29", default-features = false, features = ["tcp-tokio", "yamux", "mplex", "dns", "noise", "request-response"] }
libp2p-tokio-socks5 = "0.4"
log = { version = "0.4", features = ["serde"] }
monero = "0.9"
monero-harness = { path = "../monero-harness" }
rand = "0.7"
reqwest = { version = "0.10", default-features = false, features = ["socks"] }
serde = { version = "1", features = ["derive"] }

View File

@ -1,14 +1,18 @@
//! Run an XMR/BTC swap in the role of Alice.
//! Alice holds XMR and wishes receive BTC.
use anyhow::Result;
use async_trait::async_trait;
use genawaiter::GeneratorState;
use libp2p::{
core::{identity::Keypair, Multiaddr},
request_response::ResponseChannel,
NetworkBehaviour, PeerId,
};
use rand::rngs::OsRng;
use std::thread;
use tracing::{debug, info};
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{debug, info, warn};
use xmr_btc::alice;
mod amounts;
mod message0;
@ -17,6 +21,7 @@ mod message2;
use self::{amounts::*, message0::*, message1::*, message2::*};
use crate::{
bitcoin, monero,
network::{
peer_tracker::{self, PeerTracker},
request_response::AliceToBob,
@ -24,16 +29,46 @@ use crate::{
},
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
};
use xmr_btc::{alice::State0, bob, monero};
use xmr_btc::{
alice::{action_generator, Action, ReceiveBitcoinRedeemEncsig, State0},
bitcoin::BroadcastSignedTransaction,
bob,
monero::{CreateWalletForOutput, Transfer},
};
pub type Swarm = libp2p::Swarm<Alice>;
pub async fn swap(
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
listen: Multiaddr,
local_port: Option<u16>,
redeem_address: ::bitcoin::Address,
punish_address: ::bitcoin::Address,
) -> Result<()> {
struct Network {
swarm: Swarm,
channel: Option<ResponseChannel<AliceToBob>>,
}
impl Network {
pub fn send_message2(&mut self, proof: monero::TransferProof) {
match self.channel.take() {
None => warn!("Channel not found, did you call this twice?"),
Some(channel) => self.swarm.send_message2(channel, alice::Message2 {
tx_lock_proof: proof,
}),
}
}
}
#[async_trait]
impl ReceiveBitcoinRedeemEncsig for Network {
async fn receive_bitcoin_redeem_encsig(&mut self) -> xmr_btc::bitcoin::EncryptedSignature {
todo!()
}
}
let mut swarm = new_swarm(listen, local_port)?;
let message0: bob::Message0;
let mut last_amounts: Option<SwapAmounts> = None;
@ -96,15 +131,63 @@ pub async fn swap(
let msg = state2.next_message();
swarm.send_message1(channel, msg);
let _state3 = match swarm.next().await {
OutEvent::Message2(msg) => state2.receive(msg)?,
let (state3, channel) = match swarm.next().await {
OutEvent::Message2 { msg, channel } => {
let state3 = state2.receive(msg)?;
(state3, channel)
}
other => panic!("Unexpected event: {:?}", other),
};
info!("Handshake complete, we now have State3 for Alice.");
thread::park();
Ok(())
let network = Arc::new(Mutex::new(Network {
swarm,
channel: Some(channel),
}));
let mut action_generator =
action_generator(network.clone(), bitcoin_wallet.clone(), state3, 3600);
loop {
let state = action_generator.async_resume().await;
tracing::info!("resumed execution of alice generator, got: {:?}", state);
match state {
GeneratorState::Yielded(Action::LockXmr {
amount,
public_spend_key,
public_view_key,
}) => {
let (transfer_proof, _) = monero_wallet
.transfer(public_spend_key, public_view_key, amount)
.await?;
let mut guard = network.as_ref().lock().await;
guard.send_message2(transfer_proof);
}
GeneratorState::Yielded(Action::RedeemBtc(tx)) => {
let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?;
}
GeneratorState::Yielded(Action::CancelBtc(tx)) => {
let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?;
}
GeneratorState::Yielded(Action::PunishBtc(tx)) => {
let _ = bitcoin_wallet.broadcast_signed_transaction(tx).await?;
}
GeneratorState::Yielded(Action::CreateMoneroWalletForOutput {
spend_key,
view_key,
}) => {
monero_wallet
.create_and_load_wallet_for_output(spend_key, view_key)
.await?;
}
GeneratorState::Complete(()) => return Ok(()),
}
}
}
fn new_swarm(listen: Multiaddr, port: Option<u16>) -> Result<Swarm> {
@ -155,7 +238,10 @@ pub enum OutEvent {
msg: bob::Message1,
channel: ResponseChannel<AliceToBob>,
},
Message2(bob::Message2),
Message2 {
msg: bob::Message2,
channel: ResponseChannel<AliceToBob>,
},
}
impl From<peer_tracker::OutEvent> for OutEvent {
@ -193,7 +279,7 @@ impl From<message1::OutEvent> for OutEvent {
impl From<message2::OutEvent> for OutEvent {
fn from(event: message2::OutEvent) -> Self {
match event {
message2::OutEvent::Msg(msg) => OutEvent::Message2(msg),
message2::OutEvent::Msg { msg, channel } => OutEvent::Message2 { msg, channel },
}
}
}
@ -240,6 +326,15 @@ impl Alice {
) {
self.message1.send(channel, msg)
}
/// Send Message2 to Bob in response to receiving his Message2.
pub fn send_message2(
&mut self,
channel: ResponseChannel<AliceToBob>,
msg: xmr_btc::alice::Message2,
) {
self.message2.send(channel, msg)
}
}
impl Default for Alice {

View File

@ -18,7 +18,12 @@ use xmr_btc::bob;
#[derive(Debug)]
pub enum OutEvent {
Msg(bob::Message2),
Msg {
/// Received message from Bob.
msg: bob::Message2,
/// Channel to send back Alice's message 2.
channel: ResponseChannel<AliceToBob>,
},
}
/// A `NetworkBehaviour` that represents receiving of message 2 from Bob.
@ -78,10 +83,7 @@ impl NetworkBehaviourEventProcess<RequestResponseEvent<BobToAlice, AliceToBob>>
..
} => match request {
BobToAlice::Message2(msg) => {
self.events.push_back(OutEvent::Msg(msg));
// Send back empty response so that the request/response protocol completes.
let msg = AliceToBob::EmptyResponse;
self.rr.send_response(channel, msg);
self.events.push_back(OutEvent::Msg { msg, channel });
}
other => debug!("got request: {:?}", other),
},

View File

@ -1,12 +1,15 @@
use std::time::Duration;
use anyhow::Result;
use async_trait::async_trait;
use backoff::{future::FutureOperation as _, ExponentialBackoff};
use bitcoin::{util::psbt::PartiallySignedTransaction, Address, Transaction};
use bitcoin_harness::bitcoind_rpc::PsbtBase64;
use bitcoin_harness::{bitcoind_rpc::PsbtBase64, Bitcoind};
use reqwest::Url;
use tokio::time;
use xmr_btc::bitcoin::{
Amount, BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock, TxLock, Txid,
WatchForRawTransaction,
Amount, BlockHeight, BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock,
TransactionBlockHeight, TxLock, Txid, WatchForRawTransaction,
};
// This is cut'n'paste from xmr_btc/tests/harness/wallet/bitcoin.rs
@ -41,6 +44,22 @@ impl Wallet {
}
}
pub async fn make_wallet(
name: &str,
bitcoind: &Bitcoind<'_>,
fund_amount: Amount,
) -> Result<Wallet> {
let wallet = Wallet::new(name, &bitcoind.node_url).await?;
let buffer = Amount::from_btc(1.0).unwrap();
let amount = fund_amount + buffer;
let address = wallet.0.new_address().await.unwrap();
bitcoind.mint(address, amount).await.unwrap();
Ok(wallet)
}
#[async_trait]
impl BuildTxLockPsbt for Wallet {
async fn build_tx_lock_psbt(
@ -81,6 +100,13 @@ impl SignTxLock for Wallet {
impl BroadcastSignedTransaction for Wallet {
async fn broadcast_signed_transaction(&self, transaction: Transaction) -> Result<Txid> {
let txid = self.0.send_raw_transaction(transaction).await?;
// TODO: Instead of guessing how long it will take for the transaction to be
// mined we should ask bitcoind for the number of confirmations on `txid`
// give time for transaction to be mined
time::delay_for(Duration::from_millis(1100)).await;
Ok(txid)
}
}
@ -97,3 +123,46 @@ impl WatchForRawTransaction for Wallet {
.expect("transient errors to be retried")
}
}
#[async_trait]
impl BlockHeight for Wallet {
async fn block_height(&self) -> u32 {
(|| async { Ok(self.0.block_height().await?) })
.retry(ExponentialBackoff {
max_elapsed_time: None,
..Default::default()
})
.await
.expect("transient errors to be retried")
}
}
#[async_trait]
impl TransactionBlockHeight for Wallet {
async fn transaction_block_height(&self, txid: Txid) -> u32 {
#[derive(Debug)]
enum Error {
Io,
NotYetMined,
}
(|| async {
let block_height = self
.0
.transaction_block_height(txid)
.await
.map_err(|_| backoff::Error::Transient(Error::Io))?;
let block_height =
block_height.ok_or_else(|| backoff::Error::Transient(Error::NotYetMined))?;
Result::<_, backoff::Error<Error>>::Ok(block_height)
})
.retry(ExponentialBackoff {
max_elapsed_time: None,
..Default::default()
})
.await
.expect("transient errors to be retried")
}
}

View File

@ -21,7 +21,7 @@ use crate::{
peer_tracker::{self, PeerTracker},
transport, TokioExecutor,
},
Cmd, Never, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
};
use xmr_btc::{
alice,
@ -143,6 +143,7 @@ pub enum OutEvent {
Amounts(SwapAmounts),
Message0(alice::Message0),
Message1(alice::Message1),
Message2(alice::Message2),
}
impl From<peer_tracker::OutEvent> for OutEvent {
@ -179,9 +180,11 @@ impl From<message1::OutEvent> for OutEvent {
}
}
impl From<Never> for OutEvent {
fn from(_: Never) -> Self {
panic!("this never happens")
impl From<message2::OutEvent> for OutEvent {
fn from(event: message2::OutEvent) -> Self {
match event {
message2::OutEvent::Msg(msg) => OutEvent::Message2(msg),
}
}
}

View File

@ -7,23 +7,28 @@ use libp2p::{
NetworkBehaviour, PeerId,
};
use std::{
collections::VecDeque,
task::{Context, Poll},
time::Duration,
};
use tracing::{debug, error};
use crate::{
network::request_response::{AliceToBob, BobToAlice, Codec, Protocol, TIMEOUT},
Never,
};
use xmr_btc::bob;
use crate::network::request_response::{AliceToBob, BobToAlice, Codec, Protocol, TIMEOUT};
use xmr_btc::{alice, bob};
#[derive(Debug)]
pub enum OutEvent {
Msg(alice::Message2),
}
/// A `NetworkBehaviour` that represents sending message 2 to Alice.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "Never", poll_method = "poll")]
#[behaviour(out_event = "OutEvent", poll_method = "poll")]
#[allow(missing_debug_implementations)]
pub struct Message2 {
rr: RequestResponse<Codec>,
#[behaviour(ignore)]
events: VecDeque<OutEvent>,
}
impl Message2 {
@ -32,13 +37,15 @@ impl Message2 {
let _id = self.rr.send_request(&alice, msg);
}
// TODO: Do we need a custom implementation if we are not bubbling any out
// events?
fn poll(
&mut self,
_: &mut Context<'_>,
_: &mut impl PollParameters,
) -> Poll<NetworkBehaviourAction<RequestProtocol<Codec>, Never>> {
) -> Poll<NetworkBehaviourAction<RequestProtocol<Codec>, OutEvent>> {
if let Some(event) = self.events.pop_front() {
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event));
}
Poll::Pending
}
}
@ -55,6 +62,7 @@ impl Default for Message2 {
vec![(Protocol, ProtocolSupport::Full)],
config,
),
events: VecDeque::default(),
}
}
}
@ -70,7 +78,7 @@ impl NetworkBehaviourEventProcess<RequestResponseEvent<BobToAlice, AliceToBob>>
message: RequestResponseMessage::Response { response, .. },
..
} => match response {
AliceToBob::EmptyResponse => debug!("Alice correctly responded to message 2"),
AliceToBob::Message2(msg) => self.events.push_back(OutEvent::Msg(msg)),
other => debug!("unexpected response: {:?}", other),
},
RequestResponseEvent::InboundFailure { error, .. } => {

View File

@ -4,6 +4,7 @@ use std::fmt::{self, Display};
pub mod alice;
pub mod bitcoin;
pub mod bob;
pub mod monero;
pub mod network;
pub mod storage;
#[cfg(feature = "tor")]

View File

@ -13,20 +13,21 @@
#![forbid(unsafe_code)]
use anyhow::{bail, Context, Result};
use cli::Options;
use futures::{channel::mpsc, StreamExt};
use libp2p::Multiaddr;
use log::LevelFilter;
use std::{io, io::Write, process};
use std::{io, io::Write, process, sync::Arc};
use structopt::StructOpt;
use swap::{alice, bitcoin::Wallet, bob, Cmd, Rsp, SwapAmounts};
use tracing::info;
use url::Url;
use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock};
mod cli;
mod trace;
use cli::Options;
use swap::{alice, bitcoin, bob, monero, Cmd, Rsp, SwapAmounts};
use xmr_btc::bitcoin::{BroadcastSignedTransaction, BuildTxLockPsbt, SignTxLock};
// TODO: Add root seed file instead of generating new seed each run.
// TODO: Remove all instances of the todo! macro
@ -35,6 +36,7 @@ mod trace;
pub const PORT: u16 = 9876; // Arbitrarily chosen.
pub const ADDR: &str = "127.0.0.1";
pub const BITCOIND_JSON_RPC_URL: &str = "http://127.0.0.1:8332";
pub const MONERO_WALLET_RPC_PORT: u16 = 18083;
#[cfg(feature = "tor")]
pub const TOR_PORT: u16 = PORT + 1;
@ -70,10 +72,12 @@ async fn main() -> Result<()> {
}
let url = Url::parse(BITCOIND_JSON_RPC_URL).expect("failed to parse url");
let bitcoin_wallet = Wallet::new("alice", &url)
let bitcoin_wallet = bitcoin::Wallet::new("alice", &url)
.await
.expect("failed to create bitcoin wallet");
let monero_wallet = Arc::new(monero::Wallet::localhost(MONERO_WALLET_RPC_PORT));
let redeem = bitcoin_wallet
.new_address()
.await
@ -83,7 +87,8 @@ async fn main() -> Result<()> {
.await
.expect("failed to get new punish address");
swap_as_alice(alice.clone(), redeem, punish).await?;
let bitcoin_wallet = Arc::new(bitcoin_wallet);
swap_as_alice(bitcoin_wallet, monero_wallet, alice.clone(), redeem, punish).await?;
} else {
info!("running swap node as Bob ...");
@ -94,7 +99,7 @@ async fn main() -> Result<()> {
let alice_address = multiaddr(&alice_address)?;
let url = Url::parse(BITCOIND_JSON_RPC_URL).expect("failed to parse url");
let bitcoin_wallet = Wallet::new("bob", &url)
let bitcoin_wallet = bitcoin::Wallet::new("bob", &url)
.await
.expect("failed to create bitcoin wallet");
@ -135,24 +140,34 @@ async fn create_tor_service(
}
async fn swap_as_alice(
bitcoin_wallet: Arc<swap::bitcoin::Wallet>,
monero_wallet: Arc<swap::monero::Wallet>,
addr: Multiaddr,
redeem: bitcoin::Address,
punish: bitcoin::Address,
redeem: ::bitcoin::Address,
punish: ::bitcoin::Address,
) -> Result<()> {
#[cfg(not(feature = "tor"))]
{
alice::swap(addr, None, redeem, punish).await
alice::swap(bitcoin_wallet, monero_wallet, addr, None, redeem, punish).await
}
#[cfg(feature = "tor")]
{
alice::swap(addr, Some(PORT), redeem, punish).await
alice::swap(
bitcoin_wallet,
monero_wallet,
addr,
Some(PORT),
redeem,
punish,
)
.await
}
}
async fn swap_as_bob<W>(
sats: u64,
alice: Multiaddr,
refund: bitcoin::Address,
refund: ::bitcoin::Address,
wallet: W,
) -> Result<()>
where

132
swap/src/monero.rs Normal file
View File

@ -0,0 +1,132 @@
use anyhow::Result;
use async_trait::async_trait;
use backoff::{backoff::Constant as ConstantBackoff, future::FutureOperation as _};
use monero::{Address, Network, PrivateKey};
use monero_harness::rpc::wallet;
use std::{str::FromStr, time::Duration};
pub use xmr_btc::monero::{
Amount, CreateWalletForOutput, InsufficientFunds, PrivateViewKey, PublicKey, PublicViewKey,
Transfer, TransferProof, TxHash, WatchForTransfer, *,
};
pub struct Wallet(pub wallet::Client);
impl Wallet {
pub fn localhost(port: u16) -> Self {
Self(wallet::Client::localhost(port))
}
/// Get the balance of the primary account.
pub async fn get_balance(&self) -> Result<Amount> {
let amount = self.0.get_balance(0).await?;
Ok(Amount::from_piconero(amount))
}
}
#[async_trait]
impl Transfer for Wallet {
async fn transfer(
&self,
public_spend_key: PublicKey,
public_view_key: PublicViewKey,
amount: Amount,
) -> Result<(TransferProof, Amount)> {
let destination_address =
Address::standard(Network::Mainnet, public_spend_key, public_view_key.into());
let res = self
.0
.transfer(0, amount.as_piconero(), &destination_address.to_string())
.await?;
let tx_hash = TxHash(res.tx_hash);
let tx_key = PrivateKey::from_str(&res.tx_key)?;
let fee = Amount::from_piconero(res.fee);
Ok((TransferProof::new(tx_hash, tx_key), fee))
}
}
#[async_trait]
impl CreateWalletForOutput for Wallet {
async fn create_and_load_wallet_for_output(
&self,
private_spend_key: PrivateKey,
private_view_key: PrivateViewKey,
) -> Result<()> {
let public_spend_key = PublicKey::from_private_key(&private_spend_key);
let public_view_key = PublicKey::from_private_key(&private_view_key.into());
let address = Address::standard(Network::Mainnet, public_spend_key, public_view_key);
let _ = self
.0
.generate_from_keys(
&address.to_string(),
&private_spend_key.to_string(),
&PrivateKey::from(private_view_key).to_string(),
)
.await?;
Ok(())
}
}
#[async_trait]
impl WatchForTransfer for Wallet {
async fn watch_for_transfer(
&self,
public_spend_key: PublicKey,
public_view_key: PublicViewKey,
transfer_proof: TransferProof,
expected_amount: Amount,
expected_confirmations: u32,
) -> Result<(), InsufficientFunds> {
enum Error {
TxNotFound,
InsufficientConfirmations,
InsufficientFunds { expected: Amount, actual: Amount },
}
let address = Address::standard(Network::Mainnet, public_spend_key, public_view_key.into());
let res = (|| async {
// NOTE: Currently, this is conflating IO errors with the transaction not being
// in the blockchain yet, or not having enough confirmations on it. All these
// errors warrant a retry, but the strategy should probably differ per case
let proof = self
.0
.check_tx_key(
&String::from(transfer_proof.tx_hash()),
&transfer_proof.tx_key().to_string(),
&address.to_string(),
)
.await
.map_err(|_| backoff::Error::Transient(Error::TxNotFound))?;
if proof.received != expected_amount.as_piconero() {
return Err(backoff::Error::Permanent(Error::InsufficientFunds {
expected: expected_amount,
actual: Amount::from_piconero(proof.received),
}));
}
if proof.confirmations < expected_confirmations {
return Err(backoff::Error::Transient(Error::InsufficientConfirmations));
}
Ok(proof)
})
.retry(ConstantBackoff::new(Duration::from_secs(1)))
.await;
if let Err(Error::InsufficientFunds { expected, actual }) = res {
return Err(InsufficientFunds { expected, actual });
};
Ok(())
}
}

View File

@ -35,7 +35,6 @@ pub enum AliceToBob {
Amounts(SwapAmounts),
Message0(alice::Message0),
Message1(alice::Message1),
EmptyResponse, // This is sent back as response to Message2 from Bob.
Message2(alice::Message2),
}

View File

@ -24,7 +24,7 @@ use std::{
sync::Arc,
time::Duration,
};
use tokio::time::timeout;
use tokio::{sync::Mutex, time::timeout};
use tracing::error;
pub mod message;
@ -62,7 +62,7 @@ pub trait ReceiveBitcoinRedeemEncsig {
/// The argument `bitcoin_tx_lock_timeout` is used to determine how long we will
/// wait for Bob, the counterparty, to lock up the bitcoin.
pub fn action_generator<N, B>(
mut network: N,
network: Arc<Mutex<N>>,
bitcoin_client: Arc<B>,
// TODO: Replace this with a new, slimmer struct?
State3 {
@ -86,7 +86,7 @@ pub fn action_generator<N, B>(
bitcoin_tx_lock_timeout: u64,
) -> GenBoxed<Action, (), ()>
where
N: ReceiveBitcoinRedeemEncsig + Send + Sync + 'static,
N: ReceiveBitcoinRedeemEncsig + Send + 'static,
B: bitcoin::BlockHeight
+ bitcoin::TransactionBlockHeight
+ bitcoin::WatchForRawTransaction
@ -158,19 +158,24 @@ where
// TODO: Watch for LockXmr using watch-only wallet. Doing so will prevent Alice
// from cancelling/refunding unnecessarily.
let tx_redeem_encsig = match select(
network.receive_bitcoin_redeem_encsig(),
poll_until_btc_has_expired.clone(),
)
.await
{
Either::Left((encsig, _)) => encsig,
Either::Right(_) => {
return Err(SwapFailed::AfterXmrLock {
reason: Reason::BtcExpired,
tx_lock_height,
})
}
let tx_redeem_encsig = {
let mut guard = network.as_ref().lock().await;
let tx_redeem_encsig = match select(
guard.receive_bitcoin_redeem_encsig(),
poll_until_btc_has_expired.clone(),
)
.await
{
Either::Left((encsig, _)) => encsig,
Either::Right(_) => {
return Err(SwapFailed::AfterXmrLock {
reason: Reason::BtcExpired,
tx_lock_height,
})
}
};
tx_redeem_encsig
};
let (signed_tx_redeem, tx_redeem_txid) = {

View File

@ -31,7 +31,7 @@ pub struct Message1 {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Message2 {
pub(crate) tx_lock_proof: monero::TransferProof,
pub tx_lock_proof: monero::TransferProof,
}
impl_try_from_parent_enum!(Message0, Message);

View File

@ -1,7 +1,5 @@
pub mod harness;
use std::{convert::TryInto, sync::Arc};
use anyhow::Result;
use async_trait::async_trait;
use futures::{
@ -16,7 +14,9 @@ use harness::{
};
use monero_harness::Monero;
use rand::rngs::OsRng;
use std::{convert::TryInto, sync::Arc};
use testcontainers::clients::Cli;
use tokio::sync::Mutex;
use tracing::info;
use tracing_subscriber::util::SubscriberInitExt;
use xmr_btc::{
@ -102,7 +102,7 @@ impl Default for BobBehaviour {
}
async fn swap_as_alice(
network: AliceNetwork,
network: Arc<Mutex<AliceNetwork>>,
// FIXME: It would be more intuitive to have a single network/transport struct instead of
// splitting into two, but Rust ownership rules make this tedious
mut sender: Sender<TransferProof>,
@ -274,6 +274,8 @@ async fn on_chain_happy_path() {
let (alice_network, bob_sender) = Network::<EncryptedSignature>::new();
let (bob_network, alice_sender) = Network::<TransferProof>::new();
let alice_network = Arc::new(Mutex::new(alice_network));
try_join(
swap_as_alice(
alice_network,
@ -365,6 +367,8 @@ async fn on_chain_both_refund_if_alice_never_redeems() {
let (alice_network, bob_sender) = Network::<EncryptedSignature>::new();
let (bob_network, alice_sender) = Network::<TransferProof>::new();
let alice_network = Arc::new(Mutex::new(alice_network));
try_join(
swap_as_alice(
alice_network,
@ -460,6 +464,8 @@ async fn on_chain_alice_punishes_if_bob_never_acts_after_fund() {
let (alice_network, bob_sender) = Network::<EncryptedSignature>::new();
let (bob_network, alice_sender) = Network::<TransferProof>::new();
let alice_network = Arc::new(Mutex::new(alice_network));
let alice_swap = swap_as_alice(
alice_network,
alice_sender,