diff --git a/swap/src/network.rs b/swap/src/network.rs index 27c898b9..dd9c88eb 100644 --- a/swap/src/network.rs +++ b/swap/src/network.rs @@ -1,5 +1,6 @@ pub mod peer_tracker; pub mod request_response; +pub mod spot_price; pub mod transport; use futures::prelude::*; diff --git a/swap/src/network/request_response.rs b/swap/src/network/request_response.rs index 12cd3dc0..cfa40c26 100644 --- a/swap/src/network/request_response.rs +++ b/swap/src/network/request_response.rs @@ -13,21 +13,12 @@ pub const TIMEOUT: u64 = 3600; // One hour. /// Message receive buffer. pub const BUF_SIZE: usize = 1024 * 1024; -#[derive(Debug, Clone, Copy, Default)] -pub struct Swap; - #[derive(Debug, Clone, Copy, Default)] pub struct TransferProofProtocol; #[derive(Debug, Clone, Copy, Default)] pub struct EncryptedSignatureProtocol; -impl ProtocolName for Swap { - fn protocol_name(&self) -> &[u8] { - b"/comit/xmr/btc/swap/1.0.0" - } -} - impl ProtocolName for TransferProofProtocol { fn protocol_name(&self) -> &[u8] { b"/comit/xmr/btc/transfer_proof/1.0.0" diff --git a/swap/src/network/spot_price.rs b/swap/src/network/spot_price.rs new file mode 100644 index 00000000..5f4f34ff --- /dev/null +++ b/swap/src/network/spot_price.rs @@ -0,0 +1,66 @@ +use crate::{bitcoin, monero, network::request_response::CborCodec}; +use libp2p::{ + core::ProtocolName, + request_response::{ + ProtocolSupport, RequestResponse, RequestResponseConfig, RequestResponseEvent, + }, +}; +use serde::{Deserialize, Serialize}; + +pub type OutEvent = RequestResponseEvent; + +/// The spot price protocol allows parties to **initiate** a trade by requesting +/// a spot price. +/// +/// A spot price is binding for both parties, i.e. after the spot-price protocol +/// completes, both parties are expected to follow up with the `execution-setup` +/// protocol. +/// +/// If a party wishes to only inquire about the current price, they should use +/// the `quote` protocol instead. +#[derive(Debug, Clone, Copy, Default)] +pub struct SpotPriceProtocol; + +impl ProtocolName for SpotPriceProtocol { + fn protocol_name(&self) -> &[u8] { + b"/comit/xmr/btc/spot-price/1.0.0" + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SpotPriceRequest { + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + pub btc: bitcoin::Amount, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SpotPriceResponse { + pub xmr: monero::Amount, +} + +pub type Behaviour = + RequestResponse>; + +/// Constructs a new instance of the `spot-price` behaviour to be used by Alice. +/// +/// Alice only supports inbound connections, i.e. providing spot prices for BTC +/// in XMR. +pub fn alice() -> Behaviour { + Behaviour::new( + CborCodec::default(), + vec![(SpotPriceProtocol, ProtocolSupport::Inbound)], + RequestResponseConfig::default(), + ) +} + +/// Constructs a new instance of the `spot-price` behaviour to be used by Bob. +/// +/// Bob only supports outbound connections, i.e. requesting a spot price for a +/// given amount of BTC in XMR. +pub fn bob() -> Behaviour { + Behaviour::new( + CborCodec::default(), + vec![(SpotPriceProtocol, ProtocolSupport::Outbound)], + RequestResponseConfig::default(), + ) +} diff --git a/swap/src/protocol/alice.rs b/swap/src/protocol/alice.rs index 0cec274c..8cd9c51e 100644 --- a/swap/src/protocol/alice.rs +++ b/swap/src/protocol/alice.rs @@ -8,7 +8,6 @@ pub use self::{ behaviour::{Behaviour, OutEvent}, event_loop::{EventLoop, EventLoopHandle}, execution_setup::Message1, - quote_response::*, state::*, swap::{run, run_until}, transfer_proof::TransferProof, @@ -19,7 +18,6 @@ mod behaviour; mod encrypted_signature; pub mod event_loop; mod execution_setup; -mod quote_response; pub mod state; mod steps; pub mod swap; diff --git a/swap/src/protocol/alice/behaviour.rs b/swap/src/protocol/alice/behaviour.rs index ae168122..19cbef94 100644 --- a/swap/src/protocol/alice/behaviour.rs +++ b/swap/src/protocol/alice/behaviour.rs @@ -1,24 +1,35 @@ use crate::{ - network::{peer_tracker, peer_tracker::PeerTracker}, + bitcoin, + execution_params::ExecutionParams, + monero, + network::{ + peer_tracker, + peer_tracker::PeerTracker, + spot_price, + spot_price::{SpotPriceRequest, SpotPriceResponse}, + }, protocol::{ alice::{ - encrypted_signature, execution_setup, quote_response, transfer_proof, QuoteResponse, - State0, State3, TransferProof, + encrypted_signature, execution_setup, transfer_proof, State0, State3, TransferProof, }, - bob::{EncryptedSignature, QuoteRequest}, + bob::EncryptedSignature, }, }; -use anyhow::{Error, Result}; -use libp2p::{request_response::ResponseChannel, NetworkBehaviour, PeerId}; +use anyhow::{anyhow, Error, Result}; +use libp2p::{ + request_response::{RequestResponseMessage, ResponseChannel}, + NetworkBehaviour, PeerId, +}; +use rand::{CryptoRng, RngCore}; use tracing::debug; #[derive(Debug)] pub enum OutEvent { ConnectionEstablished(PeerId), - QuoteRequest { - msg: QuoteRequest, - channel: ResponseChannel, - bob_peer_id: PeerId, + SpotPriceRequested { + msg: SpotPriceRequest, + channel: ResponseChannel, + peer: PeerId, }, ExecutionSetupDone { bob_peer_id: PeerId, @@ -43,21 +54,37 @@ impl From for OutEvent { } } -impl From for OutEvent { - fn from(event: quote_response::OutEvent) -> Self { - use crate::protocol::alice::quote_response::OutEvent::*; +impl From for OutEvent { + fn from(event: spot_price::OutEvent) -> Self { match event { - MsgReceived { - msg, - channel, - bob_peer_id, - } => OutEvent::QuoteRequest { - msg, - channel, - bob_peer_id, - }, - ResponseSent => OutEvent::ResponseSent, - Failure(err) => OutEvent::Failure(err.context("Quote Request/Response failure")), + spot_price::OutEvent::Message { + peer, + message: + RequestResponseMessage::Request { + channel, + request: msg, + .. + }, + } => OutEvent::SpotPriceRequested { msg, channel, peer }, + spot_price::OutEvent::Message { + message: RequestResponseMessage::Response { .. }, + .. + } => OutEvent::Failure(anyhow!( + "Alice is only meant to hand out spot prices, not receive them" + )), + spot_price::OutEvent::ResponseSent { .. } => OutEvent::ResponseSent, + spot_price::OutEvent::InboundFailure { peer, error, .. } => OutEvent::Failure(anyhow!( + "spot_price protocol with peer {} failed due to {:?}", + peer, + error + )), + spot_price::OutEvent::OutboundFailure { peer, error, .. } => { + OutEvent::Failure(anyhow!( + "spot_price protocol with peer {} failed due to {:?}", + peer, + error + )) + } } } } @@ -103,29 +130,62 @@ impl From for OutEvent { } /// A `NetworkBehaviour` that represents an XMR/BTC swap node as Alice. -#[derive(NetworkBehaviour, Default)] +#[derive(NetworkBehaviour)] #[behaviour(out_event = "OutEvent", event_process = false)] #[allow(missing_debug_implementations)] pub struct Behaviour { pt: PeerTracker, - quote_response: quote_response::Behaviour, + spot_price: spot_price::Behaviour, execution_setup: execution_setup::Behaviour, transfer_proof: transfer_proof::Behaviour, encrypted_signature: encrypted_signature::Behaviour, } +impl Default for Behaviour { + fn default() -> Self { + Self { + pt: Default::default(), + spot_price: spot_price::alice(), + execution_setup: Default::default(), + transfer_proof: Default::default(), + encrypted_signature: Default::default(), + } + } +} + impl Behaviour { - pub fn send_quote_response( + pub fn send_spot_price( &mut self, - channel: ResponseChannel, - quote_response: QuoteResponse, + channel: ResponseChannel, + response: SpotPriceResponse, ) -> Result<()> { - self.quote_response.send(channel, quote_response)?; + self.spot_price + .send_response(channel, response) + .map_err(|_| anyhow!("failed to respond with spot price"))?; + Ok(()) } - pub fn start_execution_setup(&mut self, bob_peer_id: PeerId, state0: State0) { - self.execution_setup.run(bob_peer_id, state0); + pub async fn start_execution_setup( + &mut self, + peer: PeerId, + btc: bitcoin::Amount, + xmr: monero::Amount, + execution_params: ExecutionParams, + bitcoin_wallet: &bitcoin::Wallet, + rng: &mut (impl RngCore + CryptoRng), + ) -> Result<()> { + let state0 = State0::new(btc, xmr, execution_params, bitcoin_wallet, rng).await?; + + tracing::info!( + %peer, + "Starting execution setup to sell {} for {}", + xmr, btc, + ); + + self.execution_setup.run(peer, state0); + + Ok(()) } /// Send Transfer Proof to Bob. diff --git a/swap/src/protocol/alice/event_loop.rs b/swap/src/protocol/alice/event_loop.rs index 9201c594..984a8255 100644 --- a/swap/src/protocol/alice/event_loop.rs +++ b/swap/src/protocol/alice/event_loop.rs @@ -5,25 +5,21 @@ use crate::{ execution_params::ExecutionParams, monero, monero::BalanceTooLow, - network::{transport, TokioExecutor}, + network::{spot_price::SpotPriceResponse, transport, TokioExecutor}, protocol::{ alice, - alice::{ - AliceState, Behaviour, OutEvent, QuoteResponse, State0, State3, Swap, TransferProof, - }, - bob::{EncryptedSignature, QuoteRequest}, + alice::{AliceState, Behaviour, OutEvent, State3, Swap, TransferProof}, + bob::EncryptedSignature, }, seed::Seed, }; use anyhow::{bail, Context, Result}; use futures::future::RemoteHandle; -use libp2p::{ - core::Multiaddr, futures::FutureExt, request_response::ResponseChannel, PeerId, Swarm, -}; +use libp2p::{core::Multiaddr, futures::FutureExt, PeerId, Swarm}; use rand::rngs::OsRng; use std::sync::Arc; use tokio::sync::{broadcast, mpsc, mpsc::error::SendError}; -use tracing::{debug, error, info, trace}; +use tracing::{debug, error, trace}; use uuid::Uuid; #[allow(missing_debug_implementations)] @@ -166,9 +162,30 @@ where OutEvent::ConnectionEstablished(alice) => { debug!("Connection Established with {}", alice); } - OutEvent::QuoteRequest { msg, channel, bob_peer_id } => { - if let Err(error) = self.handle_quote_request(msg, channel, bob_peer_id, self.monero_wallet.clone()).await { - error!("Failed to handle quote request: {:#}", error); + OutEvent::SpotPriceRequested { msg, channel, peer } => { + let btc = msg.btc; + let xmr = match self.handle_spot_price_request(btc, self.monero_wallet.clone()).await { + Ok(xmr) => xmr, + Err(e) => { + tracing::warn!(%peer, "failed to produce spot price for {}: {:#}", btc, e); + continue; + } + }; + + match self.swarm.send_spot_price(channel, SpotPriceResponse { xmr }) { + Ok(_) => {}, + Err(e) => { + // if we can't respond, the peer probably just disconnected so it is not a huge deal, only log this on debug + debug!(%peer, "failed to respond with spot price: {:#}", e); + continue; + } + } + + match self.swarm.start_execution_setup(peer, btc, xmr, self.execution_params, self.bitcoin_wallet.as_ref(), &mut OsRng).await { + Ok(_) => {}, + Err(e) => { + tracing::warn!(%peer, "failed to start execution setup: {:#}", e); + } } } OutEvent::ExecutionSetupDone{bob_peer_id, state3} => { @@ -199,65 +216,34 @@ where } } - async fn handle_quote_request( + async fn handle_spot_price_request( &mut self, - quote_request: QuoteRequest, - channel: ResponseChannel, - bob_peer_id: PeerId, + btc: bitcoin::Amount, monero_wallet: Arc, - ) -> Result<()> { - // 1. Check if acceptable request - // 2. Send response - + ) -> Result { let rate = self .rate_service .latest_rate() .context("Failed to get latest rate")?; - let btc_amount = quote_request.btc_amount; - - if btc_amount > self.max_buy { + if btc > self.max_buy { bail!(MaximumBuyAmountExceeded { - actual: btc_amount, + actual: btc, max: self.max_buy }) } let xmr_balance = monero_wallet.get_balance().await?; let xmr_lock_fees = monero_wallet.static_tx_fee_estimate(); - let xmr_amount = rate.sell_quote(btc_amount)?; + let xmr = rate.sell_quote(btc)?; - if xmr_balance < xmr_amount + xmr_lock_fees { + if xmr_balance < xmr + xmr_lock_fees { bail!(BalanceTooLow { balance: xmr_balance }) } - let quote_response = QuoteResponse { xmr_amount }; - - self.swarm - .send_quote_response(channel, quote_response) - .context("Failed to send quote response")?; - - // 3. Start setup execution - - let state0 = State0::new( - btc_amount, - xmr_amount, - self.execution_params, - self.bitcoin_wallet.as_ref(), - &mut OsRng, - ) - .await?; - - info!( - "Starting execution setup to sell {} for {} (rate of {}) with {}", - xmr_amount, btc_amount, rate, bob_peer_id - ); - - self.swarm.start_execution_setup(bob_peer_id, state0); - // Continues once the execution setup protocol is done - Ok(()) + Ok(xmr) } async fn handle_execution_setup_done( diff --git a/swap/src/protocol/alice/quote_response.rs b/swap/src/protocol/alice/quote_response.rs deleted file mode 100644 index 474982a1..00000000 --- a/swap/src/protocol/alice/quote_response.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::{ - monero, - network::request_response::{CborCodec, Swap, TIMEOUT}, - protocol::bob::QuoteRequest, -}; -use anyhow::{anyhow, Error, Result}; -use libp2p::{ - request_response::{ - ProtocolSupport, RequestResponse, RequestResponseConfig, RequestResponseEvent, - RequestResponseMessage, ResponseChannel, - }, - NetworkBehaviour, PeerId, -}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tracing::debug; - -#[derive(Debug)] -pub enum OutEvent { - MsgReceived { - msg: QuoteRequest, - channel: ResponseChannel, - bob_peer_id: PeerId, - }, - ResponseSent, - Failure(Error), -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct QuoteResponse { - pub xmr_amount: monero::Amount, -} - -impl From> for OutEvent { - fn from(event: RequestResponseEvent) -> Self { - match event { - RequestResponseEvent::Message { - peer, - message: - RequestResponseMessage::Request { - request, channel, .. - }, - .. - } => { - debug!("Received quote request from {}", peer); - OutEvent::MsgReceived { - msg: request, - channel, - bob_peer_id: peer, - } - } - RequestResponseEvent::Message { - message: RequestResponseMessage::Response { .. }, - .. - } => OutEvent::Failure(anyhow!("Alice should not get a Response")), - RequestResponseEvent::InboundFailure { error, .. } => { - OutEvent::Failure(anyhow!("Inbound failure: {:?}", error)) - } - RequestResponseEvent::OutboundFailure { error, .. } => { - OutEvent::Failure(anyhow!("Outbound failure: {:?}", error)) - } - RequestResponseEvent::ResponseSent { peer, .. } => { - tracing::debug!("successfully sent quote response to {}", peer); - OutEvent::ResponseSent - } - } - } -} - -/// A `NetworkBehaviour` that represents negotiate a swap using Swap -/// request/response. -#[derive(NetworkBehaviour)] -#[behaviour(out_event = "OutEvent", event_process = false)] -#[allow(missing_debug_implementations)] -pub struct Behaviour { - rr: RequestResponse>, -} - -impl Behaviour { - /// Alice always sends her messages as a response to a request from Bob. - pub fn send( - &mut self, - channel: ResponseChannel, - msg: QuoteResponse, - ) -> Result<()> { - self.rr - .send_response(channel, msg) - .map_err(|_| anyhow!("failed to send quote response"))?; - - Ok(()) - } -} - -impl Default for Behaviour { - fn default() -> Self { - let timeout = Duration::from_secs(TIMEOUT); - - let mut config = RequestResponseConfig::default(); - config.set_request_timeout(timeout); - - Self { - rr: RequestResponse::new( - CborCodec::default(), - vec![(Swap, ProtocolSupport::Inbound)], - config, - ), - } - } -} diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index d69fbb83..6d90de80 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -1,15 +1,22 @@ -//! Run an XMR/BTC swap in the role of Bob. -//! Bob holds BTC and wishes receive XMR. use crate::{ bitcoin, database::Database, execution_params::ExecutionParams, monero, - network::peer_tracker::{self, PeerTracker}, - protocol::{alice, alice::TransferProof, bob}, + network::{ + peer_tracker::{self, PeerTracker}, + spot_price, + spot_price::{SpotPriceRequest, SpotPriceResponse}, + }, + protocol::{alice::TransferProof, bob}, +}; +use anyhow::{anyhow, Error, Result}; +pub use execution_setup::{Message0, Message2, Message4}; +use libp2p::{ + core::Multiaddr, + request_response::{RequestResponseMessage, ResponseChannel}, + NetworkBehaviour, PeerId, }; -use anyhow::{Error, Result}; -use libp2p::{core::Multiaddr, NetworkBehaviour, PeerId}; use std::sync::Arc; use tracing::debug; use uuid::Uuid; @@ -18,19 +25,15 @@ pub use self::{ cancel::cancel, encrypted_signature::EncryptedSignature, event_loop::{EventLoop, EventLoopHandle}, - quote_request::*, refund::refund, state::*, swap::{run, run_until}, }; -pub use execution_setup::{Message0, Message2, Message4}; -use libp2p::request_response::ResponseChannel; pub mod cancel; mod encrypted_signature; pub mod event_loop; mod execution_setup; -mod quote_request; pub mod refund; pub mod state; pub mod swap; @@ -119,7 +122,7 @@ impl Builder { #[derive(Debug)] pub enum OutEvent { ConnectionEstablished(PeerId), - QuoteResponse(alice::QuoteResponse), + SpotPriceReceived(SpotPriceResponse), ExecutionSetupDone(Result>), TransferProof { msg: Box, @@ -140,12 +143,34 @@ impl From for OutEvent { } } -impl From for OutEvent { - fn from(event: quote_request::OutEvent) -> Self { - use quote_request::OutEvent::*; +impl From for OutEvent { + fn from(event: spot_price::OutEvent) -> Self { match event { - MsgReceived(quote_response) => OutEvent::QuoteResponse(quote_response), - Failure(err) => OutEvent::CommunicationError(err.context("Failure with Quote Request")), + spot_price::OutEvent::Message { + message: RequestResponseMessage::Response { response, .. }, + .. + } => OutEvent::SpotPriceReceived(response), + spot_price::OutEvent::Message { + message: RequestResponseMessage::Request { .. }, + .. + } => OutEvent::CommunicationError(anyhow!( + "Bob is only meant to receive spot prices, not hand them out" + )), + spot_price::OutEvent::ResponseSent { .. } => OutEvent::ResponseSent, + spot_price::OutEvent::InboundFailure { peer, error, .. } => { + OutEvent::CommunicationError(anyhow!( + "spot_price protocol with peer {} failed due to {:?}", + peer, + error + )) + } + spot_price::OutEvent::OutboundFailure { peer, error, .. } => { + OutEvent::CommunicationError(anyhow!( + "spot_price protocol with peer {} failed due to {:?}", + peer, + error + )) + } } } } @@ -187,21 +212,32 @@ impl From for OutEvent { } /// A `NetworkBehaviour` that represents an XMR/BTC swap node as Bob. -#[derive(NetworkBehaviour, Default)] +#[derive(NetworkBehaviour)] #[behaviour(out_event = "OutEvent", event_process = false)] #[allow(missing_debug_implementations)] pub struct Behaviour { pt: PeerTracker, - quote_request: quote_request::Behaviour, + spot_price: spot_price::Behaviour, execution_setup: execution_setup::Behaviour, transfer_proof: transfer_proof::Behaviour, encrypted_signature: encrypted_signature::Behaviour, } +impl Default for Behaviour { + fn default() -> Self { + Self { + pt: Default::default(), + spot_price: spot_price::bob(), + execution_setup: Default::default(), + transfer_proof: Default::default(), + encrypted_signature: Default::default(), + } + } +} + impl Behaviour { - /// Sends a quote request to Alice to retrieve the rate. - pub fn send_quote_request(&mut self, alice: PeerId, quote_request: QuoteRequest) { - let _ = self.quote_request.send(alice, quote_request); + pub fn request_spot_price(&mut self, alice: PeerId, request: SpotPriceRequest) { + let _ = self.spot_price.send_request(&alice, request); } pub fn start_execution_setup( diff --git a/swap/src/protocol/bob/event_loop.rs b/swap/src/protocol/bob/event_loop.rs index 44011600..21d26a14 100644 --- a/swap/src/protocol/bob/event_loop.rs +++ b/swap/src/protocol/bob/event_loop.rs @@ -1,10 +1,14 @@ use crate::{ bitcoin, bitcoin::EncryptedSignature, - network::{transport, TokioExecutor}, + monero, + network::{ + spot_price::{SpotPriceRequest, SpotPriceResponse}, + transport, TokioExecutor, + }, protocol::{ - alice::{QuoteResponse, TransferProof}, - bob::{Behaviour, OutEvent, QuoteRequest, State0, State2}, + alice::TransferProof, + bob::{Behaviour, OutEvent, State0, State2}, }, }; use anyhow::{anyhow, bail, Context, Result}; @@ -35,24 +39,17 @@ impl Default for Channels { #[derive(Debug)] pub struct EventLoopHandle { - recv_quote_response: Receiver, + recv_spot_price: Receiver, start_execution_setup: Sender, done_execution_setup: Receiver>, recv_transfer_proof: Receiver, conn_established: Receiver, dial_alice: Sender<()>, - send_quote_request: Sender, + request_spot_price: Sender, send_encrypted_signature: Sender, } impl EventLoopHandle { - pub async fn recv_quote_response(&mut self) -> Result { - self.recv_quote_response - .recv() - .await - .ok_or_else(|| anyhow!("Failed to receive quote response from Alice")) - } - pub async fn execution_setup(&mut self, state0: State0) -> Result { let _ = self.start_execution_setup.send(state0).await?; @@ -82,9 +79,19 @@ impl EventLoopHandle { Ok(()) } - pub async fn send_quote_request(&mut self, quote_request: QuoteRequest) -> Result<()> { - let _ = self.send_quote_request.send(quote_request).await?; - Ok(()) + pub async fn request_spot_price(&mut self, btc: bitcoin::Amount) -> Result { + let _ = self + .request_spot_price + .send(SpotPriceRequest { btc }) + .await?; + + let response = self + .recv_spot_price + .recv() + .await + .ok_or_else(|| anyhow!("Failed to receive spot price from Alice"))?; + + Ok(response.xmr) } pub async fn send_encrypted_signature( @@ -102,13 +109,13 @@ pub struct EventLoop { swarm: libp2p::Swarm, bitcoin_wallet: Arc, alice_peer_id: PeerId, - recv_quote_response: Sender, + request_spot_price: Receiver, + recv_spot_price: Sender, start_execution_setup: Receiver, done_execution_setup: Sender>, recv_transfer_proof: Sender, dial_alice: Receiver<()>, conn_established: Sender, - send_quote_request: Receiver, send_encrypted_signature: Receiver, } @@ -147,24 +154,24 @@ impl EventLoop { swarm, alice_peer_id, bitcoin_wallet, - recv_quote_response: quote_response.sender, + recv_spot_price: quote_response.sender, start_execution_setup: start_execution_setup.receiver, done_execution_setup: done_execution_setup.sender, recv_transfer_proof: recv_transfer_proof.sender, conn_established: conn_established.sender, dial_alice: dial_alice.receiver, - send_quote_request: send_quote_request.receiver, + request_spot_price: send_quote_request.receiver, send_encrypted_signature: send_encrypted_signature.receiver, }; let handle = EventLoopHandle { - recv_quote_response: quote_response.receiver, + recv_spot_price: quote_response.receiver, start_execution_setup: start_execution_setup.sender, done_execution_setup: done_execution_setup.receiver, recv_transfer_proof: recv_transfer_proof.receiver, conn_established: conn_established.receiver, dial_alice: dial_alice.sender, - send_quote_request: send_quote_request.sender, + request_spot_price: send_quote_request.sender, send_encrypted_signature: send_encrypted_signature.sender, }; @@ -179,8 +186,8 @@ impl EventLoop { OutEvent::ConnectionEstablished(peer_id) => { let _ = self.conn_established.send(peer_id).await; } - OutEvent::QuoteResponse(msg) => { - let _ = self.recv_quote_response.send(msg).await; + OutEvent::SpotPriceReceived(msg) => { + let _ = self.recv_spot_price.send(msg).await; }, OutEvent::ExecutionSetupDone(res) => { let _ = self.done_execution_setup.send(res.map(|state|*state)).await; @@ -213,9 +220,9 @@ impl EventLoop { } } }, - quote_request = self.send_quote_request.recv().fuse() => { + quote_request = self.request_spot_price.recv().fuse() => { if let Some(quote_request) = quote_request { - self.swarm.send_quote_request(self.alice_peer_id, quote_request); + self.swarm.request_spot_price(self.alice_peer_id, quote_request); } }, option = self.start_execution_setup.recv().fuse() => { diff --git a/swap/src/protocol/bob/quote_request.rs b/swap/src/protocol/bob/quote_request.rs deleted file mode 100644 index 23d8dd3c..00000000 --- a/swap/src/protocol/bob/quote_request.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - network::request_response::{CborCodec, Swap, TIMEOUT}, - protocol::alice::QuoteResponse, -}; -use anyhow::{anyhow, Error, Result}; -use libp2p::{ - request_response::{ - ProtocolSupport, RequestId, RequestResponse, RequestResponseConfig, RequestResponseEvent, - RequestResponseMessage, - }, - NetworkBehaviour, PeerId, -}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tracing::debug; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct QuoteRequest { - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - pub btc_amount: bitcoin::Amount, -} - -#[derive(Debug)] -pub enum OutEvent { - MsgReceived(QuoteResponse), - Failure(Error), -} - -/// A `NetworkBehaviour` that represents doing the negotiation of a swap. -#[derive(NetworkBehaviour)] -#[behaviour(out_event = "OutEvent", event_process = false)] -#[allow(missing_debug_implementations)] -pub struct Behaviour { - rr: RequestResponse>, -} - -impl Behaviour { - pub fn send(&mut self, alice: PeerId, quote_request: QuoteRequest) -> Result { - debug!("Requesting quote for {}", quote_request.btc_amount); - - let id = self.rr.send_request(&alice, quote_request); - - Ok(id) - } -} - -impl Default for Behaviour { - fn default() -> Self { - let timeout = Duration::from_secs(TIMEOUT); - - let mut config = RequestResponseConfig::default(); - config.set_request_timeout(timeout); - - Self { - rr: RequestResponse::new( - CborCodec::default(), - vec![(Swap, ProtocolSupport::Outbound)], - config, - ), - } - } -} - -impl From> for OutEvent { - fn from(event: RequestResponseEvent) -> Self { - match event { - RequestResponseEvent::Message { - message: RequestResponseMessage::Request { .. }, - .. - } => OutEvent::Failure(anyhow!("Bob should never get a request from Alice")), - RequestResponseEvent::Message { - message: RequestResponseMessage::Response { response, .. }, - .. - } => OutEvent::MsgReceived(response), - RequestResponseEvent::InboundFailure { error, .. } => { - OutEvent::Failure(anyhow!("Inbound failure: {:?}", error)) - } - RequestResponseEvent::OutboundFailure { error, .. } => { - OutEvent::Failure(anyhow!("Outbound failure: {:?}", error)) - } - RequestResponseEvent::ResponseSent { .. } => { - OutEvent::Failure(anyhow!("Bob does not send a quote response to Alice")) - } - } - } -} diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 35b495de..7b712e29 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -5,7 +5,7 @@ use crate::{ execution_params::ExecutionParams, monero, monero::InsufficientFunds, - protocol::bob::{self, event_loop::EventLoopHandle, state::*, QuoteRequest}, + protocol::bob::{self, event_loop::EventLoopHandle, state::*}, }; use anyhow::{bail, Result}; use async_recursion::async_recursion; @@ -72,7 +72,7 @@ async fn run_until_internal( event_loop_handle.dial().await?; - let state2 = request_quote_and_setup( + let state2 = request_price_and_setup( btc_amount, &mut event_loop_handle, execution_params, @@ -394,24 +394,20 @@ async fn run_until_internal( } } -pub async fn request_quote_and_setup( - btc_amount: bitcoin::Amount, +pub async fn request_price_and_setup( + btc: bitcoin::Amount, event_loop_handle: &mut EventLoopHandle, execution_params: ExecutionParams, bitcoin_refund_address: bitcoin::Address, ) -> Result { - event_loop_handle - .send_quote_request(QuoteRequest { btc_amount }) - .await?; + let xmr = event_loop_handle.request_spot_price(btc).await?; - let xmr_amount = event_loop_handle.recv_quote_response().await?.xmr_amount; - - tracing::info!("Quote for {} is {}", btc_amount, xmr_amount); + tracing::info!("Spot price for {} is {}", btc, xmr); let state0 = State0::new( &mut OsRng, - btc_amount, - xmr_amount, + btc, + xmr, execution_params.bitcoin_cancel_timelock, execution_params.bitcoin_punish_timelock, bitcoin_refund_address,