From 6b1443b5700393bcb9eb5e8f31a3a2b373c06cb7 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 4 Feb 2021 17:40:52 +1100 Subject: [PATCH] wip: Process swap request in event loop --- Cargo.lock | 5 +- swap/Cargo.toml | 2 +- swap/src/monero.rs | 14 ++++- swap/src/protocol/alice.rs | 3 + swap/src/protocol/alice/event_loop.rs | 75 +++++++++++++++++++----- swap/src/protocol/alice/swap_response.rs | 10 +++- 6 files changed, 86 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed5439a9..86464966 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2917,10 +2917,11 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.8.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e81662973c7a8d9663e64a0de4cd642b89a21d64966e3d99606efdc5fb0cc6" +checksum = "04d1fde955d206c00af1eb529d8ebfca3b505921820a0436566dbcc6c4d4ffb3" dependencies = [ + "arrayvec", "num-traits", "serde", ] diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 5cb515da..34bf5ecb 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -40,7 +40,7 @@ pem = "0.8" prettytable-rs = "0.8" rand = "0.7" reqwest = { version = "0.11", default-features = false } -rust_decimal = "1.8" +rust_decimal = "1.10" serde = { version = "1", features = ["derive"] } serde_cbor = "0.11" serde_derive = "1.0" diff --git a/swap/src/monero.rs b/swap/src/monero.rs index caec8864..a3484f9d 100644 --- a/swap/src/monero.rs +++ b/swap/src/monero.rs @@ -15,6 +15,7 @@ use rust_decimal::{ }; use serde::{Deserialize, Serialize}; use std::{ + convert::TryFrom, fmt::Display, ops::{Add, Mul, Sub}, str::FromStr, @@ -88,13 +89,22 @@ impl Amount { self.0 } + pub fn from_monero(amount: f64) -> Result { + let decimal = Decimal::try_from(amount)?; + Self::from_decimal(decimal) + } + pub fn parse_monero(amount: &str) -> Result { let decimal = Decimal::from_str(amount)?; + Self::from_decimal(decimal) + } + + fn from_decimal(amount: Decimal) -> Result { let piconeros_dec = - decimal.mul(Decimal::from_u64(PICONERO_OFFSET).expect("constant to fit into u64")); + amount.mul(Decimal::from_u64(PICONERO_OFFSET).expect("constant to fit into u64")); let piconeros = piconeros_dec .to_u64() - .ok_or_else(|| OverflowError(amount.to_owned()))?; + .ok_or_else(|| OverflowError(amount.to_string()))?; Ok(Amount(piconeros)) } } diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 5923c6cd..9285e45d 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -196,6 +196,9 @@ impl Builder { alice_behaviour, self.listen_address(), self.peer_id, + self.execution_params.bitcoin_cancel_timelock, + self.execution_params.bitcoin_punish_timelock, + self.bitcoin_wallet.clone(), ) } } diff --git a/swap/src/protocol/alice/event_loop.rs b/swap/src/protocol/alice/event_loop.rs index b3e4ec08..6b1826f2 100644 --- a/swap/src/protocol/alice/event_loop.rs +++ b/swap/src/protocol/alice/event_loop.rs @@ -1,7 +1,10 @@ use crate::{ + bitcoin, + bitcoin::Timelock, + monero, network::{request_response::Response, transport::SwapTransport, TokioExecutor}, protocol::{ - alice::{Behaviour, OutEvent, State0, State3, SwapResponse, TransferProof}, + alice::{swap_response, Behaviour, OutEvent, State0, State3, SwapResponse, TransferProof}, bob::EncryptedSignature, }, }; @@ -9,9 +12,14 @@ use anyhow::{anyhow, Context, Result}; use libp2p::{ core::Multiaddr, futures::FutureExt, request_response::ResponseChannel, PeerId, Swarm, }; +use rand::rngs::OsRng; +use std::sync::Arc; use tokio::sync::mpsc::{Receiver, Sender}; use tracing::{debug, error, trace}; +// TODO: Use dynamic +const RATE: u32 = 100; + #[allow(missing_debug_implementations)] pub struct Channels { sender: Sender, @@ -35,7 +43,6 @@ impl Default for Channels { pub struct EventLoopHandle { done_execution_setup: Receiver>, recv_encrypted_signature: Receiver, - request: Receiver, send_swap_response: Sender<(ResponseChannel, SwapResponse)>, start_execution_setup: Sender<(PeerId, State0)>, send_transfer_proof: Sender<(PeerId, TransferProof)>, @@ -62,15 +69,6 @@ impl EventLoopHandle { .ok_or_else(|| anyhow!("Failed to receive Bitcoin encrypted signature from Bob")) } - pub async fn recv_request( - &mut self, - ) -> Result { - self.request - .recv() - .await - .ok_or_else(|| anyhow!("Failed to receive amounts request from Bob")) - } - pub async fn send_swap_response( &mut self, channel: ResponseChannel, @@ -100,10 +98,12 @@ pub struct EventLoop { start_execution_setup: Receiver<(PeerId, State0)>, done_execution_setup: Sender>, recv_encrypted_signature: Sender, - request: Sender, send_swap_response: Receiver<(ResponseChannel, SwapResponse)>, send_transfer_proof: Receiver<(PeerId, TransferProof)>, recv_transfer_proof_ack: Sender<()>, + cancel_timelock: Timelock, + punish_timelock: Timelock, + bitcoin_wallet: Arc, } impl EventLoop { @@ -112,6 +112,10 @@ impl EventLoop { behaviour: Behaviour, listen: Multiaddr, peer_id: PeerId, + // TODO(Franck): Have the timelocks in a struct so they don't get swap by mistake + cancel_timelock: Timelock, + punish_timelock: Timelock, + bitcoin_wallet: Arc, ) -> Result<(Self, EventLoopHandle)> { let mut swarm = libp2p::swarm::SwarmBuilder::new(transport, behaviour, peer_id) .executor(Box::new(TokioExecutor { @@ -125,7 +129,6 @@ impl EventLoop { let start_execution_setup = Channels::new(); let done_execution_setup = Channels::new(); let recv_encrypted_signature = Channels::new(); - let request = Channels::new(); let send_swap_response = Channels::new(); let send_transfer_proof = Channels::new(); let recv_transfer_proof_ack = Channels::new(); @@ -135,17 +138,18 @@ impl EventLoop { start_execution_setup: start_execution_setup.receiver, done_execution_setup: done_execution_setup.sender, recv_encrypted_signature: recv_encrypted_signature.sender, - request: request.sender, send_swap_response: send_swap_response.receiver, send_transfer_proof: send_transfer_proof.receiver, recv_transfer_proof_ack: recv_transfer_proof_ack.sender, + cancel_timelock, + punish_timelock, + bitcoin_wallet, }; let handle = EventLoopHandle { start_execution_setup: start_execution_setup.sender, done_execution_setup: done_execution_setup.receiver, recv_encrypted_signature: recv_encrypted_signature.receiver, - request: request.receiver, send_swap_response: send_swap_response.sender, send_transfer_proof: send_transfer_proof.sender, recv_transfer_proof_ack: recv_transfer_proof_ack.receiver, @@ -173,7 +177,7 @@ impl EventLoop { let _ = self.recv_encrypted_signature.send(*msg).await; } OutEvent::Request(event) => { - let _ = self.request.send(*event).await; + let _ = self.handle_swap_request(*event).await; } } }, @@ -200,4 +204,43 @@ impl EventLoop { } } } + + async fn handle_swap_request(&mut self, event: swap_response::OutEvent) -> Result<()> { + let swap_response::OutEvent { + msg, + bob_peer_id, + channel, + } = event; + // 1. Check if acceptable request + // 2. Send response + + let btc_amount = msg.btc_amount; + let xmr_amount = btc_amount.as_btc() * RATE as f64; + let xmr_amount = monero::Amount::from_monero(xmr_amount)?; + let swap_response = SwapResponse { xmr_amount }; + + self.swarm + .send_swap_response(channel, swap_response) + .context("Failed to send swap response")?; + + // 3. Start setup execution + + let state0 = State0::new( + btc_amount, + xmr_amount, + self.cancel_timelock, + self.punish_timelock, + self.bitcoin_wallet.as_ref(), + &mut OsRng, + ) + .await?; + + self.swarm.execution_setup.run(bob_peer_id, state0); + + // 4. Trigger swap + + todo!(); + + Ok(()) + } } diff --git a/swap/src/protocol/alice/swap_response.rs b/swap/src/protocol/alice/swap_response.rs index 98b23a51..7864e210 100644 --- a/swap/src/protocol/alice/swap_response.rs +++ b/swap/src/protocol/alice/swap_response.rs @@ -10,7 +10,7 @@ use libp2p::{ RequestResponseEvent, RequestResponseMessage, ResponseChannel, }, swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters}, - NetworkBehaviour, + NetworkBehaviour, PeerId, }; use serde::{Deserialize, Serialize}; use std::{ @@ -23,6 +23,7 @@ use tracing::{debug, error}; #[derive(Debug)] pub struct OutEvent { pub msg: bob::SwapRequest, + pub bob_peer_id: PeerId, pub channel: ResponseChannel, } @@ -86,6 +87,7 @@ impl NetworkBehaviourEventProcess> for B fn inject_event(&mut self, event: RequestResponseEvent) { match event { RequestResponseEvent::Message { + peer, message: RequestResponseMessage::Request { request, channel, .. @@ -94,7 +96,11 @@ impl NetworkBehaviourEventProcess> for B } => { if let Request::SwapRequest(msg) = request { debug!("Received swap request"); - self.events.push_back(OutEvent { msg: *msg, channel }) + self.events.push_back(OutEvent { + bob_peer_id: peer, + msg: *msg, + channel, + }) } } RequestResponseEvent::Message {