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.
This commit is contained in:
rishflab 2020-12-10 11:18:45 +11:00
parent 3d8866f1a0
commit 27d1334726
6 changed files with 114 additions and 133 deletions

View File

@ -145,9 +145,6 @@ pub async fn swap(
punish_address, punish_address,
); );
info!("Commencing handshake");
swarm.set_state0(state.clone());
state0 = Some(state) state0 = Some(state)
} }
OutEvent::Message0(msg) => { OutEvent::Message0(msg) => {
@ -387,6 +384,20 @@ pub struct Behaviour {
} }
impl 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 { pub fn identity(&self) -> Keypair {
self.identity.clone() self.identity.clone()
} }
@ -402,13 +413,6 @@ impl Behaviour {
info!("Sent amounts response"); 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. /// Send Message1 to Bob in response to receiving his Message1.
pub fn send_message1( pub fn send_message1(
&mut self, &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 { fn calculate_amounts(btc: ::bitcoin::Amount) -> SwapAmounts {
// TODO (Franck): This should instead verify that the received amounts matches // TODO (Franck): This should instead verify that the received amounts matches
// the command line arguments This value corresponds to 100 XMR per BTC // the command line arguments This value corresponds to 100 XMR per BTC

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
alice::swarm_driver::SwarmDriverHandle, bitcoin, monero, network::request_response::AliceToBob, alice::swarm_driver::SwarmDriverHandle, bitcoin, monero, network::request_response::AliceToBob,
SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK, SwapAmounts,
}; };
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic}; use ecdsa_fun::{adaptor::Adaptor, nonce::Deterministic};
@ -9,13 +9,14 @@ use futures::{
pin_mut, pin_mut,
}; };
use libp2p::request_response::ResponseChannel; use libp2p::request_response::ResponseChannel;
use sha2::Sha256; use sha2::Sha256;
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use tokio::time::timeout; use tokio::time::timeout;
use tracing::trace; use tracing::trace;
use xmr_btc::{ use xmr_btc::{
alice, alice,
alice::{State0, State3}, alice::State3,
bitcoin::{ bitcoin::{
poll_until_block_height_is_gte, BlockHeight, BroadcastSignedTransaction, poll_until_block_height_is_gte, BlockHeight, BroadcastSignedTransaction,
EncryptedSignature, GetRawTransaction, TransactionBlockHeight, TxCancel, TxLock, TxRefund, EncryptedSignature, GetRawTransaction, TransactionBlockHeight, TxCancel, TxLock, TxRefund,
@ -27,20 +28,23 @@ use xmr_btc::{
}; };
pub async fn negotiate( pub async fn negotiate(
state0: xmr_btc::alice::State0,
amounts: SwapAmounts, amounts: SwapAmounts,
a: bitcoin::SecretKey, // a: bitcoin::SecretKey,
s_a: cross_curve_dleq::Scalar, // s_a: cross_curve_dleq::Scalar,
v_a: monero::PrivateViewKey, // v_a: monero::PrivateViewKey,
swarm: &mut SwarmDriverHandle, swarm_handle: &mut SwarmDriverHandle,
bitcoin_wallet: Arc<bitcoin::Wallet>, // bitcoin_wallet: Arc<bitcoin::Wallet>,
config: Config, config: Config,
) -> Result<(ResponseChannel<AliceToBob>, State3)> { ) -> Result<(ResponseChannel<AliceToBob>, State3)> {
trace!("Starting negotiate"); 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 .await
.context("Failed to receive dial connection from Bob")??; .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 .await
.context("Failed to receive amounts from Bob")??; .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 bob_message0 = timeout(config.bob_time_to_act, swarm_handle.recv_message0()).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 state1 = state0.receive(bob_message0)?; 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); 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)?; let state3 = state2.receive(bob_message2)?;

View File

@ -1,4 +1,3 @@
use anyhow::{bail, Result};
use libp2p::{ use libp2p::{
request_response::{ request_response::{
handler::RequestProtocol, ProtocolSupport, RequestResponse, RequestResponseConfig, handler::RequestProtocol, ProtocolSupport, RequestResponse, RequestResponseConfig,
@ -32,17 +31,24 @@ pub struct Message0 {
#[behaviour(ignore)] #[behaviour(ignore)]
events: VecDeque<OutEvent>, events: VecDeque<OutEvent>,
#[behaviour(ignore)] #[behaviour(ignore)]
state: Option<State0>, state: State0,
} }
impl Message0 { impl Message0 {
pub fn set_state(&mut self, state: State0) -> Result<()> { pub fn new(state: State0) -> Self {
if self.state.is_some() { let timeout = Duration::from_secs(TIMEOUT);
bail!("Trying to set state a second time"); let mut config = RequestResponseConfig::default();
} config.set_request_timeout(timeout);
self.state = Some(state);
Ok(()) Self {
rr: RequestResponse::new(
Codec::default(),
vec![(Message0Protocol, ProtocolSupport::Full)],
config,
),
events: Default::default(),
state,
}
} }
fn poll( 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<RequestResponseEvent<BobToAlice, AliceToBob>> for Message0 { impl NetworkBehaviourEventProcess<RequestResponseEvent<BobToAlice, AliceToBob>> for Message0 {
fn inject_event(&mut self, event: RequestResponseEvent<BobToAlice, AliceToBob>) { fn inject_event(&mut self, event: RequestResponseEvent<BobToAlice, AliceToBob>) {
match event { match event {
@ -88,13 +76,9 @@ impl NetworkBehaviourEventProcess<RequestResponseEvent<BobToAlice, AliceToBob>>
} => { } => {
if let BobToAlice::Message0(msg) = request { if let BobToAlice::Message0(msg) = request {
debug!("Received Message0"); debug!("Received Message0");
let response = match &self.state {
None => panic!("No state, did you forget to set it?"), let response = AliceToBob::Message0(self.state.next_message(&mut OsRng));
Some(state) => {
// TODO: Get OsRng from somewhere?
AliceToBob::Message0(state.next_message(&mut OsRng))
}
};
self.rr.send_response(channel, response); self.rr.send_response(channel, response);
debug!("Sent Message0"); debug!("Sent Message0");

View File

@ -10,9 +10,7 @@ use crate::{
}, },
swarm_driver::SwarmDriverHandle, swarm_driver::SwarmDriverHandle,
}, },
bitcoin,
bitcoin::EncryptedSignature, bitcoin::EncryptedSignature,
monero,
network::request_response::AliceToBob, network::request_response::AliceToBob,
SwapAmounts, SwapAmounts,
}; };
@ -27,10 +25,9 @@ use rand::{CryptoRng, RngCore};
use std::{fmt, sync::Arc}; use std::{fmt, sync::Arc};
use tracing::info; use tracing::info;
use xmr_btc::{ use xmr_btc::{
alice::State3, alice::{State0, State3},
bitcoin::{TransactionBlockHeight, TxCancel, TxRefund, WatchForRawTransaction}, bitcoin::{TransactionBlockHeight, TxCancel, TxRefund, WatchForRawTransaction},
config::Config, config::Config,
cross_curve_dleq,
monero::CreateWalletForOutput, monero::CreateWalletForOutput,
}; };
@ -44,9 +41,7 @@ impl<T> Rng for T where T: RngCore + CryptoRng + Send {}
pub enum AliceState { pub enum AliceState {
Started { Started {
amounts: SwapAmounts, amounts: SwapAmounts,
a: bitcoin::SecretKey, state0: State0,
s_a: cross_curve_dleq::Scalar,
v_a: monero::PrivateViewKey,
}, },
Negotiated { Negotiated {
channel: ResponseChannel<AliceToBob>, channel: ResponseChannel<AliceToBob>,
@ -157,22 +152,8 @@ pub async fn run_until(
Ok((state, swarm)) Ok((state, swarm))
} else { } else {
match state { match state {
AliceState::Started { AliceState::Started { amounts, state0 } => {
amounts, let (channel, state3) = negotiate(state0, amounts, &mut swarm, config).await?;
a,
s_a,
v_a,
} => {
let (channel, state3) = negotiate(
amounts,
a,
s_a,
v_a,
&mut swarm,
bitcoin_wallet.clone(),
config,
)
.await?;
run_until( run_until(
AliceState::Negotiated { AliceState::Negotiated {

View File

@ -16,6 +16,7 @@ use anyhow::Result;
use futures::{channel::mpsc, StreamExt}; use futures::{channel::mpsc, StreamExt};
use libp2p::Multiaddr; use libp2p::Multiaddr;
use prettytable::{row, Table}; use prettytable::{row, Table};
use rand::rngs::OsRng;
use std::{io, io::Write, process, sync::Arc}; use std::{io, io::Write, process, sync::Arc};
use structopt::StructOpt; use structopt::StructOpt;
use swap::{ use swap::{
@ -25,9 +26,10 @@ use swap::{
network::transport::{build, build_tor, SwapTransport}, network::transport::{build, build_tor, SwapTransport},
recover::recover, recover::recover,
storage::Database, storage::Database,
Cmd, Rsp, SwapAmounts, Cmd, Rsp, SwapAmounts, PUNISH_TIMELOCK, REFUND_TIMELOCK,
}; };
use tracing::info; use tracing::info;
use xmr_btc::{alice::State0, cross_curve_dleq};
#[macro_use] #[macro_use]
extern crate prettytable; extern crate prettytable;
@ -50,7 +52,34 @@ async fn main() -> Result<()> {
} => { } => {
info!("running swap node as Alice ..."); 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 local_key_pair = behaviour.identity();
let (listen_addr, _ac, transport) = match tor_port { 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( swap_as_alice(
bitcoin_wallet, bitcoin_wallet,
monero_wallet, monero_wallet,

View File

@ -12,7 +12,7 @@ use tempfile::tempdir;
use testcontainers::clients::Cli; use testcontainers::clients::Cli;
use tracing_subscriber::util::SubscriberInitExt as _; use tracing_subscriber::util::SubscriberInitExt as _;
use uuid::Uuid; 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 /// 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() { async fn happy_path() {
use tracing_subscriber::util::SubscriberInitExt as _; use tracing_subscriber::util::SubscriberInitExt as _;
let _guard = tracing_subscriber::fmt() 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) .with_ansi(false)
.set_default(); .set_default();
@ -249,22 +249,38 @@ async fn init_alice(
xmr: xmr_to_swap, xmr: xmr_to_swap,
}; };
let alice_behaviour = alice::Behaviour::default(); let (alice_state, alice_behaviour) = {
let alice_peer_id = alice_behaviour.peer_id(); let rng = &mut OsRng;
let alice_transport = build(alice_behaviour.identity()).unwrap();
let rng = &mut OsRng;
let alice_state = {
let a = bitcoin::SecretKey::new_random(rng); let a = bitcoin::SecretKey::new_random(rng);
let s_a = cross_curve_dleq::Scalar::random(rng); let s_a = cross_curve_dleq::Scalar::random(rng);
let v_a = xmr_btc::monero::PrivateViewKey::new_random(rng); let v_a = xmr_btc::monero::PrivateViewKey::new_random(rng);
AliceState::Started { let redeem_address = alice_btc_wallet.as_ref().new_address().await.unwrap();
amounts, let punish_address = redeem_address.clone();
let state0 = State0::new(
a, a,
s_a, s_a,
v_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) = let (swarm_driver, handle) =
alice::swarm_driver::SwarmDriver::new(alice_transport, alice_behaviour, listen).unwrap(); alice::swarm_driver::SwarmDriver::new(alice_transport, alice_behaviour, listen).unwrap();