From 27d133472662bdea2f0604b32a855b6d98193ecd Mon Sep 17 00:00:00 2001 From: rishflab Date: Thu, 10 Dec 2020 11:18:45 +1100 Subject: [PATCH] Init Alice behaviour with state0 Previously state0 had to be set after creating Alice's behaviour. With the event loop we no longer has access to the swarm so set_state0() has to be called indirectly through a channel. This means it is difficult to guarantee state0 is being set due to the asynchronous nature of channels. This was solved by initialising Alice with state0. --- swap/src/alice.rs | 40 ++++++++++----------------- swap/src/alice/execution.rs | 54 +++++++++++++++---------------------- swap/src/alice/message0.rs | 50 ++++++++++++---------------------- swap/src/alice/swap.rs | 27 +++---------------- swap/src/bin/swap.rs | 40 ++++++++++++++++++++------- swap/tests/e2e.rs | 36 ++++++++++++++++++------- 6 files changed, 114 insertions(+), 133 deletions(-) diff --git a/swap/src/alice.rs b/swap/src/alice.rs index 769e4e6f..72d3ec0c 100644 --- a/swap/src/alice.rs +++ b/swap/src/alice.rs @@ -145,9 +145,6 @@ pub async fn swap( punish_address, ); - info!("Commencing handshake"); - swarm.set_state0(state.clone()); - state0 = Some(state) } OutEvent::Message0(msg) => { @@ -387,6 +384,20 @@ pub struct Behaviour { } impl Behaviour { + pub fn new(state: State0) -> Self { + let identity = Keypair::generate_ed25519(); + + Self { + pt: PeerTracker::default(), + amounts: Amounts::default(), + message0: Message0::new(state), + message1: Message1::default(), + message2: Message2::default(), + message3: Message3::default(), + identity, + } + } + pub fn identity(&self) -> Keypair { self.identity.clone() } @@ -402,13 +413,6 @@ impl Behaviour { info!("Sent amounts response"); } - // TODO(Franck) remove - /// Message0 gets sent within the network layer using this state0. - pub fn set_state0(&mut self, state: State0) { - debug!("Set state 0"); - let _ = self.message0.set_state(state); - } - /// Send Message1 to Bob in response to receiving his Message1. pub fn send_message1( &mut self, @@ -430,22 +434,6 @@ impl Behaviour { } } -impl Default for Behaviour { - fn default() -> Self { - let identity = Keypair::generate_ed25519(); - - Self { - pt: PeerTracker::default(), - amounts: Amounts::default(), - message0: Message0::default(), - message1: Message1::default(), - message2: Message2::default(), - message3: Message3::default(), - identity, - } - } -} - fn calculate_amounts(btc: ::bitcoin::Amount) -> SwapAmounts { // TODO (Franck): This should instead verify that the received amounts matches // the command line arguments This value corresponds to 100 XMR per BTC diff --git a/swap/src/alice/execution.rs b/swap/src/alice/execution.rs index 43ffbcc7..e3e718fe 100644 --- a/swap/src/alice/execution.rs +++ b/swap/src/alice/execution.rs @@ -1,6 +1,6 @@ use crate::{ alice::swarm_driver::SwarmDriverHandle, bitcoin, monero, network::request_response::AliceToBob, - SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, + SwapAmounts, }; use anyhow::{bail, Context, Result}; use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic}; @@ -9,13 +9,14 @@ use futures::{ pin_mut, }; use libp2p::request_response::ResponseChannel; + use sha2::Sha256; use std::{sync::Arc, time::Duration}; use tokio::time::timeout; use tracing::trace; use xmr_btc::{ alice, - alice::{State0, State3}, + alice::State3, bitcoin::{ poll_until_block_height_is_gte, BlockHeight, BroadcastSignedTransaction, EncryptedSignature, GetRawTransaction, TransactionBlockHeight, TxCancel, TxLock, TxRefund, @@ -27,20 +28,23 @@ use xmr_btc::{ }; pub async fn negotiate( + state0: xmr_btc::alice::State0, amounts: SwapAmounts, - a: bitcoin::SecretKey, - s_a: cross_curve_dleq::Scalar, - v_a: monero::PrivateViewKey, - swarm: &mut SwarmDriverHandle, - bitcoin_wallet: Arc, + // a: bitcoin::SecretKey, + // s_a: cross_curve_dleq::Scalar, + // v_a: monero::PrivateViewKey, + swarm_handle: &mut SwarmDriverHandle, + // bitcoin_wallet: Arc, config: Config, ) -> Result<(ResponseChannel, State3)> { trace!("Starting negotiate"); - let _peer_id = timeout(config.bob_time_to_act, swarm.recv_conn_established()) + + // todo: we can move this out, we dont need to timeout here + let _peer_id = timeout(config.bob_time_to_act, swarm_handle.recv_conn_established()) .await .context("Failed to receive dial connection from Bob")??; - let event = timeout(config.bob_time_to_act, swarm.recv_request()) + let event = timeout(config.bob_time_to_act, swarm_handle.recv_request()) .await .context("Failed to receive amounts from Bob")??; @@ -52,37 +56,23 @@ pub async fn negotiate( ); } - swarm.send_amounts(event.channel, amounts).await?; + swarm_handle.send_amounts(event.channel, amounts).await?; - let redeem_address = bitcoin_wallet.as_ref().new_address().await?; - let punish_address = redeem_address.clone(); - - let state0 = State0::new( - a, - s_a, - v_a, - amounts.btc, - amounts.xmr, - REFUND_TIMELOCK, - PUNISH_TIMELOCK, - redeem_address, - punish_address, - ); - - // TODO(Franck): Understand why this is needed. - // swarm.swarm.set_state0(state0.clone()); - - let bob_message0 = timeout(config.bob_time_to_act, swarm.recv_message0()).await??; + let bob_message0 = timeout(config.bob_time_to_act, swarm_handle.recv_message0()).await??; let state1 = state0.receive(bob_message0)?; - let (bob_message1, channel) = timeout(config.bob_time_to_act, swarm.recv_message1()).await??; + let (bob_message1, channel) = + timeout(config.bob_time_to_act, swarm_handle.recv_message1()).await??; let state2 = state1.receive(bob_message1); - swarm.send_message1(channel, state2.next_message()).await?; + swarm_handle + .send_message1(channel, state2.next_message()) + .await?; - let (bob_message2, channel) = timeout(config.bob_time_to_act, swarm.recv_message2()).await??; + let (bob_message2, channel) = + timeout(config.bob_time_to_act, swarm_handle.recv_message2()).await??; let state3 = state2.receive(bob_message2)?; diff --git a/swap/src/alice/message0.rs b/swap/src/alice/message0.rs index b10b3aca..f9989f4f 100644 --- a/swap/src/alice/message0.rs +++ b/swap/src/alice/message0.rs @@ -1,4 +1,3 @@ -use anyhow::{bail, Result}; use libp2p::{ request_response::{ handler::RequestProtocol, ProtocolSupport, RequestResponse, RequestResponseConfig, @@ -32,17 +31,24 @@ pub struct Message0 { #[behaviour(ignore)] events: VecDeque, #[behaviour(ignore)] - state: Option, + state: State0, } impl Message0 { - pub fn set_state(&mut self, state: State0) -> Result<()> { - if self.state.is_some() { - bail!("Trying to set state a second time"); - } - self.state = Some(state); + pub fn new(state: State0) -> Self { + let timeout = Duration::from_secs(TIMEOUT); + let mut config = RequestResponseConfig::default(); + config.set_request_timeout(timeout); - Ok(()) + Self { + rr: RequestResponse::new( + Codec::default(), + vec![(Message0Protocol, ProtocolSupport::Full)], + config, + ), + events: Default::default(), + state, + } } fn poll( @@ -58,24 +64,6 @@ impl Message0 { } } -impl Default for Message0 { - fn default() -> Self { - let timeout = Duration::from_secs(TIMEOUT); - let mut config = RequestResponseConfig::default(); - config.set_request_timeout(timeout); - - Self { - rr: RequestResponse::new( - Codec::default(), - vec![(Message0Protocol, ProtocolSupport::Full)], - config, - ), - events: Default::default(), - state: None, - } - } -} - impl NetworkBehaviourEventProcess> for Message0 { fn inject_event(&mut self, event: RequestResponseEvent) { match event { @@ -88,13 +76,9 @@ impl NetworkBehaviourEventProcess> } => { if let BobToAlice::Message0(msg) = request { debug!("Received Message0"); - let response = match &self.state { - None => panic!("No state, did you forget to set it?"), - Some(state) => { - // TODO: Get OsRng from somewhere? - AliceToBob::Message0(state.next_message(&mut OsRng)) - } - }; + + let response = AliceToBob::Message0(self.state.next_message(&mut OsRng)); + self.rr.send_response(channel, response); debug!("Sent Message0"); diff --git a/swap/src/alice/swap.rs b/swap/src/alice/swap.rs index ca5d84dd..a12ba94e 100644 --- a/swap/src/alice/swap.rs +++ b/swap/src/alice/swap.rs @@ -10,9 +10,7 @@ use crate::{ }, swarm_driver::SwarmDriverHandle, }, - bitcoin, bitcoin::EncryptedSignature, - monero, network::request_response::AliceToBob, SwapAmounts, }; @@ -27,10 +25,9 @@ use rand::{CryptoRng, RngCore}; use std::{fmt, sync::Arc}; use tracing::info; use xmr_btc::{ - alice::State3, + alice::{State0, State3}, bitcoin::{TransactionBlockHeight, TxCancel, TxRefund, WatchForRawTransaction}, config::Config, - cross_curve_dleq, monero::CreateWalletForOutput, }; @@ -44,9 +41,7 @@ impl Rng for T where T: RngCore + CryptoRng + Send {} pub enum AliceState { Started { amounts: SwapAmounts, - a: bitcoin::SecretKey, - s_a: cross_curve_dleq::Scalar, - v_a: monero::PrivateViewKey, + state0: State0, }, Negotiated { channel: ResponseChannel, @@ -157,22 +152,8 @@ pub async fn run_until( Ok((state, swarm)) } else { match state { - AliceState::Started { - amounts, - a, - s_a, - v_a, - } => { - let (channel, state3) = negotiate( - amounts, - a, - s_a, - v_a, - &mut swarm, - bitcoin_wallet.clone(), - config, - ) - .await?; + AliceState::Started { amounts, state0 } => { + let (channel, state3) = negotiate(state0, amounts, &mut swarm, config).await?; run_until( AliceState::Negotiated { diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index c6e3d82d..80b526c0 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -16,6 +16,7 @@ use anyhow::Result; use futures::{channel::mpsc, StreamExt}; use libp2p::Multiaddr; use prettytable::{row, Table}; +use rand::rngs::OsRng; use std::{io, io::Write, process, sync::Arc}; use structopt::StructOpt; use swap::{ @@ -25,9 +26,10 @@ use swap::{ network::transport::{build, build_tor, SwapTransport}, recover::recover, storage::Database, - Cmd, Rsp, SwapAmounts, + Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, }; use tracing::info; +use xmr_btc::{alice::State0, cross_curve_dleq}; #[macro_use] extern crate prettytable; @@ -50,7 +52,34 @@ async fn main() -> Result<()> { } => { info!("running swap node as Alice ..."); - let behaviour = alice::Behaviour::default(); + let bitcoin_wallet = bitcoin::Wallet::new("alice", bitcoind_url) + .await + .expect("failed to create bitcoin wallet"); + let bitcoin_wallet = Arc::new(bitcoin_wallet); + + let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); + + let rng = &mut OsRng; + let a = bitcoin::SecretKey::new_random(rng); + let s_a = cross_curve_dleq::Scalar::random(rng); + let v_a = xmr_btc::monero::PrivateViewKey::new_random(rng); + let redeem_address = bitcoin_wallet.as_ref().new_address().await?; + let punish_address = redeem_address.clone(); + let state0 = State0::new( + a, + s_a, + v_a, + // todo: get from CLI args + bitcoin::Amount::from_sat(100), + // todo: get from CLI args + monero::Amount::from_piconero(1000000), + REFUND_TIMELOCK, + PUNISH_TIMELOCK, + redeem_address, + punish_address, + ); + + let behaviour = alice::Behaviour::new(state0); let local_key_pair = behaviour.identity(); let (listen_addr, _ac, transport) = match tor_port { @@ -72,13 +101,6 @@ async fn main() -> Result<()> { } }; - let bitcoin_wallet = bitcoin::Wallet::new("alice", bitcoind_url) - .await - .expect("failed to create bitcoin wallet"); - let bitcoin_wallet = Arc::new(bitcoin_wallet); - - let monero_wallet = Arc::new(monero::Wallet::new(monerod_url)); - swap_as_alice( bitcoin_wallet, monero_wallet, diff --git a/swap/tests/e2e.rs b/swap/tests/e2e.rs index f277f0b1..f1886c91 100644 --- a/swap/tests/e2e.rs +++ b/swap/tests/e2e.rs @@ -12,7 +12,7 @@ use tempfile::tempdir; use testcontainers::clients::Cli; use tracing_subscriber::util::SubscriberInitExt as _; use uuid::Uuid; -use xmr_btc::{bitcoin, config::Config, cross_curve_dleq}; +use xmr_btc::{alice::State0, bitcoin, config::Config, cross_curve_dleq}; /// Run the following tests with RUST_MIN_STACK=10000000 @@ -20,7 +20,7 @@ use xmr_btc::{bitcoin, config::Config, cross_curve_dleq}; async fn happy_path() { use tracing_subscriber::util::SubscriberInitExt as _; let _guard = tracing_subscriber::fmt() - .with_env_filter("swap=trace,xmr_btc=trace") + .with_env_filter("swap=trace,xmr_btc=trace,monero_harness=info") .with_ansi(false) .set_default(); @@ -249,22 +249,38 @@ async fn init_alice( xmr: xmr_to_swap, }; - let alice_behaviour = alice::Behaviour::default(); - let alice_peer_id = alice_behaviour.peer_id(); - let alice_transport = build(alice_behaviour.identity()).unwrap(); - let rng = &mut OsRng; - let alice_state = { + let (alice_state, alice_behaviour) = { + let rng = &mut OsRng; let a = bitcoin::SecretKey::new_random(rng); let s_a = cross_curve_dleq::Scalar::random(rng); let v_a = xmr_btc::monero::PrivateViewKey::new_random(rng); - AliceState::Started { - amounts, + let redeem_address = alice_btc_wallet.as_ref().new_address().await.unwrap(); + let punish_address = redeem_address.clone(); + let state0 = State0::new( a, s_a, v_a, - } + amounts.btc, + amounts.xmr, + REFUND_TIMELOCK, + PUNISH_TIMELOCK, + redeem_address, + punish_address, + ); + + // let msg0 = AliceToBob::Message0(self.state.next_message(&mut OsRng)); + ( + AliceState::Started { + amounts, + state0: state0.clone(), + }, + alice::Behaviour::new(state0), + ) }; + let alice_peer_id = alice_behaviour.peer_id(); + let alice_transport = build(alice_behaviour.identity()).unwrap(); + let (swarm_driver, handle) = alice::swarm_driver::SwarmDriver::new(alice_transport, alice_behaviour, listen).unwrap();