Use bmrng to model communicaton of Bob's EventLoop with the handle

bmrng is a library providing a request-response channel that allows
the receiving end of the channel to send a response back to the sender.
This allows us to more accurately implement the functions on the
`EventLoopHandle`. In particular, we now _wait_ for the ACK of specific
messages from the other party before resolving the future.

For example, when sending the encrypted signature, the async function
on the `EventLoopHandle` does not resolve until we received the ACK
from the other party.

We also delete the `Channels` abstraction in favor of directly creating
bmrng channels. This allows us to directly control the channel buffer
which we set to 1 because we don't need more than that on Bob's side.
pull/387/head
Thomas Eizinger 3 years ago
parent 958e5b12bc
commit 1c47b32681
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96

51
Cargo.lock generated

@ -481,6 +481,17 @@ dependencies = [
"byte-tools", "byte-tools",
] ]
[[package]]
name = "bmrng"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7ad917af2fc43efa0b20d2bf3b2c1bd1090fa2a6b8c73847458c8335dea2b"
dependencies = [
"futures",
"loom",
"tokio 1.4.0",
]
[[package]] [[package]]
name = "bs58" name = "bs58"
version = "0.4.0" version = "0.4.0"
@ -1181,6 +1192,19 @@ dependencies = [
"byteorder", "byteorder",
] ]
[[package]]
name = "generator"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "061d3be1afec479d56fa3bd182bf966c7999ec175fcfdb87ac14d417241366c6"
dependencies = [
"cc",
"libc",
"log 0.4.14",
"rustversion",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.12.4" version = "0.12.4"
@ -1909,6 +1933,20 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]]
name = "loom"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44c73b4636e497b4917eb21c33539efa3816741a2d3ff26c6316f1b529481a4"
dependencies = [
"cfg-if 1.0.0",
"futures-util",
"generator",
"scoped-tls",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.6.5" version = "0.6.5"
@ -3014,6 +3052,12 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "rustversion"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
[[package]] [[package]]
name = "rw-stream-sink" name = "rw-stream-sink"
version = "0.2.1" version = "0.2.1"
@ -3037,6 +3081,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -3522,6 +3572,7 @@ dependencies = [
"big-bytes", "big-bytes",
"bitcoin", "bitcoin",
"bitcoin-harness", "bitcoin-harness",
"bmrng",
"config", "config",
"conquer-once", "conquer-once",
"curve25519-dalek", "curve25519-dalek",

@ -18,6 +18,7 @@ base64 = "0.13"
bdk = { version = "0.5" } bdk = { version = "0.5" }
big-bytes = "1" big-bytes = "1"
bitcoin = { version = "0.26", features = ["rand", "use-serde"] } bitcoin = { version = "0.26", features = ["rand", "use-serde"] }
bmrng = "0.5"
config = { version = "0.11", default-features = false, features = ["toml"] } config = { version = "0.11", default-features = false, features = ["toml"] }
conquer-once = "0.3" conquer-once = "0.3"
curve25519-dalek = "3" curve25519-dalek = "3"

@ -5,7 +5,9 @@ use crate::protocol::bob;
use crate::{bitcoin, monero}; use crate::{bitcoin, monero};
use anyhow::{anyhow, Error, Result}; use anyhow::{anyhow, Error, Result};
use libp2p::core::Multiaddr; use libp2p::core::Multiaddr;
use libp2p::request_response::{RequestResponseEvent, RequestResponseMessage, ResponseChannel}; use libp2p::request_response::{
RequestId, RequestResponseEvent, RequestResponseMessage, ResponseChannel,
};
use libp2p::{NetworkBehaviour, PeerId}; use libp2p::{NetworkBehaviour, PeerId};
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
@ -107,14 +109,22 @@ impl Builder {
#[derive(Debug)] #[derive(Debug)]
pub enum OutEvent { pub enum OutEvent {
QuoteReceived(BidQuote), QuoteReceived {
SpotPriceReceived(spot_price::Response), id: RequestId,
ExecutionSetupDone(Result<Box<State2>>), response: BidQuote,
},
SpotPriceReceived {
id: RequestId,
response: spot_price::Response,
},
ExecutionSetupDone(Box<Result<State2>>),
TransferProofReceived { TransferProofReceived {
msg: Box<transfer_proof::Request>, msg: Box<transfer_proof::Request>,
channel: ResponseChannel<()>, channel: ResponseChannel<()>,
}, },
EncryptedSignatureAcknowledged, EncryptedSignatureAcknowledged {
id: RequestId,
},
ResponseSent, // Same variant is used for all messages as no processing is done ResponseSent, // Same variant is used for all messages as no processing is done
CommunicationError(Error), CommunicationError(Error),
} }
@ -133,7 +143,13 @@ impl From<quote::Message> for OutEvent {
fn from(message: quote::Message) -> Self { fn from(message: quote::Message) -> Self {
match message { match message {
quote::Message::Request { .. } => OutEvent::unexpected_request(), quote::Message::Request { .. } => OutEvent::unexpected_request(),
quote::Message::Response { response, .. } => OutEvent::QuoteReceived(response), quote::Message::Response {
response,
request_id,
} => OutEvent::QuoteReceived {
id: request_id,
response,
},
} }
} }
} }
@ -142,7 +158,13 @@ impl From<spot_price::Message> for OutEvent {
fn from(message: spot_price::Message) -> Self { fn from(message: spot_price::Message) -> Self {
match message { match message {
spot_price::Message::Request { .. } => OutEvent::unexpected_request(), spot_price::Message::Request { .. } => OutEvent::unexpected_request(),
spot_price::Message::Response { response, .. } => OutEvent::SpotPriceReceived(response), spot_price::Message::Response {
response,
request_id,
} => OutEvent::SpotPriceReceived {
id: request_id,
response,
},
} }
} }
} }
@ -165,8 +187,8 @@ impl From<encrypted_signature::Message> for OutEvent {
fn from(message: encrypted_signature::Message) -> Self { fn from(message: encrypted_signature::Message) -> Self {
match message { match message {
encrypted_signature::Message::Request { .. } => OutEvent::unexpected_request(), encrypted_signature::Message::Request { .. } => OutEvent::unexpected_request(),
encrypted_signature::Message::Response { .. } => { encrypted_signature::Message::Response { request_id, .. } => {
OutEvent::EncryptedSignatureAcknowledged OutEvent::EncryptedSignatureAcknowledged { id: request_id }
} }
} }
} }
@ -221,7 +243,7 @@ where
impl From<execution_setup::OutEvent> for OutEvent { impl From<execution_setup::OutEvent> for OutEvent {
fn from(event: execution_setup::OutEvent) -> Self { fn from(event: execution_setup::OutEvent) -> Self {
match event { match event {
execution_setup::OutEvent::Done(res) => OutEvent::ExecutionSetupDone(res.map(Box::new)), execution_setup::OutEvent::Done(res) => OutEvent::ExecutionSetupDone(Box::new(res)),
} }
} }
} }

@ -3,27 +3,46 @@ use crate::network::quote::BidQuote;
use crate::network::{encrypted_signature, spot_price, transfer_proof}; use crate::network::{encrypted_signature, spot_price, transfer_proof};
use crate::protocol::bob::{Behaviour, OutEvent, State0, State2}; use crate::protocol::bob::{Behaviour, OutEvent, State0, State2};
use crate::{bitcoin, monero}; use crate::{bitcoin, monero};
use anyhow::{anyhow, Result}; use anyhow::{Context, Result};
use futures::FutureExt; use futures::future::{BoxFuture, OptionFuture};
use futures::{FutureExt, StreamExt};
use libp2p::request_response::{RequestId, ResponseChannel};
use libp2p::swarm::SwarmEvent; use libp2p::swarm::SwarmEvent;
use libp2p::{PeerId, Swarm}; use libp2p::{PeerId, Swarm};
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::{Receiver, Sender}; use std::time::Duration;
use tracing::{debug, error};
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct EventLoop { pub struct EventLoop {
swarm: libp2p::Swarm<Behaviour>, swarm: libp2p::Swarm<Behaviour>,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<bitcoin::Wallet>,
alice_peer_id: PeerId, alice_peer_id: PeerId,
request_spot_price: Receiver<spot_price::Request>,
recv_spot_price: Sender<spot_price::Response>, // these streams represents outgoing requests that we have to make
start_execution_setup: Receiver<State0>, quote_requests: bmrng::RequestReceiverStream<(), BidQuote>,
done_execution_setup: Sender<Result<State2>>, spot_price_requests: bmrng::RequestReceiverStream<spot_price::Request, spot_price::Response>,
recv_transfer_proof: Sender<transfer_proof::Request>, encrypted_signature_requests: bmrng::RequestReceiverStream<encrypted_signature::Request, ()>,
send_encrypted_signature: Receiver<encrypted_signature::Request>, execution_setup_requests: bmrng::RequestReceiverStream<State0, Result<State2>>,
request_quote: Receiver<()>,
recv_quote: Sender<BidQuote>, // these represents requests that are currently in-flight.
// once we get a response to a matching [`RequestId`], we will use the responder to relay the
// response.
inflight_spot_price_requests: HashMap<RequestId, bmrng::Responder<spot_price::Response>>,
inflight_quote_requests: HashMap<RequestId, bmrng::Responder<BidQuote>>,
inflight_encrypted_signature_requests: HashMap<RequestId, bmrng::Responder<()>>,
inflight_execution_setup: Option<bmrng::Responder<Result<State2>>>,
/// The sender we will use to relay incoming transfer proofs.
transfer_proof: bmrng::RequestSender<transfer_proof::Request, ()>,
/// The future representing the successful handling of an incoming transfer
/// proof.
///
/// Once we've sent a transfer proof to the ongoing swap, this future waits
/// until the swap took it "out" of the `EventLoopHandle`. As this future
/// resolves, we use the `ResponseChannel` returned from it to send an ACK
/// to Alice that we have successfully processed the transfer proof.
pending_transfer_proof: OptionFuture<BoxFuture<'static, ResponseChannel<()>>>,
} }
impl EventLoop { impl EventLoop {
@ -32,38 +51,34 @@ impl EventLoop {
alice_peer_id: PeerId, alice_peer_id: PeerId,
bitcoin_wallet: Arc<bitcoin::Wallet>, bitcoin_wallet: Arc<bitcoin::Wallet>,
) -> Result<(Self, EventLoopHandle)> { ) -> Result<(Self, EventLoopHandle)> {
let start_execution_setup = Channels::new(); let execution_setup = bmrng::channel_with_timeout(1, Duration::from_secs(30));
let done_execution_setup = Channels::new(); let transfer_proof = bmrng::channel_with_timeout(1, Duration::from_secs(30));
let recv_transfer_proof = Channels::new(); let encrypted_signature = bmrng::channel_with_timeout(1, Duration::from_secs(30));
let send_encrypted_signature = Channels::new(); let spot_price = bmrng::channel_with_timeout(1, Duration::from_secs(30));
let request_spot_price = Channels::new(); let quote = bmrng::channel_with_timeout(1, Duration::from_secs(30));
let recv_spot_price = Channels::new();
let request_quote = Channels::new();
let recv_quote = Channels::new();
let event_loop = EventLoop { let event_loop = EventLoop {
swarm, swarm,
alice_peer_id, alice_peer_id,
bitcoin_wallet, bitcoin_wallet,
start_execution_setup: start_execution_setup.receiver, execution_setup_requests: execution_setup.1.into(),
done_execution_setup: done_execution_setup.sender, transfer_proof: transfer_proof.0,
recv_transfer_proof: recv_transfer_proof.sender, encrypted_signature_requests: encrypted_signature.1.into(),
send_encrypted_signature: send_encrypted_signature.receiver, spot_price_requests: spot_price.1.into(),
request_spot_price: request_spot_price.receiver, quote_requests: quote.1.into(),
recv_spot_price: recv_spot_price.sender, inflight_spot_price_requests: HashMap::default(),
request_quote: request_quote.receiver, inflight_quote_requests: HashMap::default(),
recv_quote: recv_quote.sender, inflight_execution_setup: None,
inflight_encrypted_signature_requests: HashMap::default(),
pending_transfer_proof: OptionFuture::from(None),
}; };
let handle = EventLoopHandle { let handle = EventLoopHandle {
start_execution_setup: start_execution_setup.sender, execution_setup: execution_setup.0,
done_execution_setup: done_execution_setup.receiver, transfer_proof: transfer_proof.1,
recv_transfer_proof: recv_transfer_proof.receiver, encrypted_signature: encrypted_signature.0,
send_encrypted_signature: send_encrypted_signature.sender, spot_price: spot_price.0,
request_spot_price: request_spot_price.sender, quote: quote.0,
recv_spot_price: recv_spot_price.receiver,
request_quote: request_quote.sender,
recv_quote: recv_quote.receiver,
}; };
Ok((event_loop, handle)) Ok((event_loop, handle))
@ -76,24 +91,40 @@ impl EventLoop {
tokio::select! { tokio::select! {
swarm_event = self.swarm.next_event().fuse() => { swarm_event = self.swarm.next_event().fuse() => {
match swarm_event { match swarm_event {
SwarmEvent::Behaviour(OutEvent::SpotPriceReceived(msg)) => { SwarmEvent::Behaviour(OutEvent::SpotPriceReceived { id, response }) => {
let _ = self.recv_spot_price.send(msg).await; if let Some(responder) = self.inflight_spot_price_requests.remove(&id) {
} let _ = responder.respond(response);
SwarmEvent::Behaviour(OutEvent::QuoteReceived(msg)) => { }
let _ = self.recv_quote.send(msg).await;
} }
SwarmEvent::Behaviour(OutEvent::ExecutionSetupDone(res)) => { SwarmEvent::Behaviour(OutEvent::QuoteReceived { id, response }) => {
let _ = self.done_execution_setup.send(res.map(|state|*state)).await; if let Some(responder) = self.inflight_quote_requests.remove(&id) {
let _ = responder.respond(response);
}
} }
SwarmEvent::Behaviour(OutEvent::TransferProofReceived{ msg, channel }) => { SwarmEvent::Behaviour(OutEvent::ExecutionSetupDone(response)) => {
let _ = self.recv_transfer_proof.send(*msg).await; if let Some(responder) = self.inflight_execution_setup.take() {
// Send back empty response so that the request/response protocol completes. let _ = responder.respond(*response);
if let Err(error) = self.swarm.transfer_proof.send_response(channel, ()) {
error!("Failed to send Transfer Proof ack: {:?}", error);
} }
} }
SwarmEvent::Behaviour(OutEvent::EncryptedSignatureAcknowledged) => { SwarmEvent::Behaviour(OutEvent::TransferProofReceived { msg, channel }) => {
debug!("Alice acknowledged encrypted signature"); let mut responder = match self.transfer_proof.send(*msg).await {
Ok(responder) => responder,
Err(_) => {
tracing::warn!("Failed to pass on transfer proof");
continue;
}
};
self.pending_transfer_proof = OptionFuture::from(Some(async move {
let _ = responder.recv().await;
channel
}.boxed()));
}
SwarmEvent::Behaviour(OutEvent::EncryptedSignatureAcknowledged { id }) => {
if let Some(responder) = self.inflight_encrypted_signature_requests.remove(&id) {
let _ = responder.respond(());
}
} }
SwarmEvent::Behaviour(OutEvent::ResponseSent) => { SwarmEvent::Behaviour(OutEvent::ResponseSent) => {
@ -133,27 +164,26 @@ impl EventLoop {
_ => {} _ => {}
} }
}, },
spot_price_request = self.request_spot_price.recv().fuse() => { Some((request, responder)) = self.spot_price_requests.next().fuse() => {
if let Some(request) = spot_price_request { let id = self.swarm.spot_price.send_request(&self.alice_peer_id, request);
self.swarm.spot_price.send_request(&self.alice_peer_id, request); self.inflight_spot_price_requests.insert(id, responder);
}
}, },
quote_request = self.request_quote.recv().fuse() => { Some(((), responder)) = self.quote_requests.next().fuse() => {
if quote_request.is_some() { let id = self.swarm.quote.send_request(&self.alice_peer_id, ());
self.swarm.quote.send_request(&self.alice_peer_id, ()); self.inflight_quote_requests.insert(id, responder);
}
}, },
option = self.start_execution_setup.recv().fuse() => { Some((request, responder)) = self.execution_setup_requests.next().fuse() => {
if let Some(state0) = option { self.swarm.execution_setup.run(self.alice_peer_id, request, self.bitcoin_wallet.clone());
let _ = self self.inflight_execution_setup = Some(responder);
.swarm
.execution_setup.run(self.alice_peer_id, state0, self.bitcoin_wallet.clone());
}
}, },
encrypted_signature = self.send_encrypted_signature.recv().fuse() => { Some((request, responder)) = self.encrypted_signature_requests.next().fuse() => {
if let Some(tx_redeem_encsig) = encrypted_signature { let id = self.swarm.encrypted_signature.send_request(&self.alice_peer_id, request);
self.swarm.encrypted_signature.send_request(&self.alice_peer_id, tx_redeem_encsig); self.inflight_encrypted_signature_requests.insert(id, responder);
} },
Some(response_channel) = &mut self.pending_transfer_proof => {
let _ = self.swarm.transfer_proof.send_response(response_channel, ());
self.pending_transfer_proof = OptionFuture::from(None);
} }
} }
} }
@ -162,87 +192,50 @@ impl EventLoop {
#[derive(Debug)] #[derive(Debug)]
pub struct EventLoopHandle { pub struct EventLoopHandle {
start_execution_setup: Sender<State0>, execution_setup: bmrng::RequestSender<State0, Result<State2>>,
done_execution_setup: Receiver<Result<State2>>, transfer_proof: bmrng::RequestReceiver<transfer_proof::Request, ()>,
recv_transfer_proof: Receiver<transfer_proof::Request>, encrypted_signature: bmrng::RequestSender<encrypted_signature::Request, ()>,
send_encrypted_signature: Sender<encrypted_signature::Request>, spot_price: bmrng::RequestSender<spot_price::Request, spot_price::Response>,
request_spot_price: Sender<spot_price::Request>, quote: bmrng::RequestSender<(), BidQuote>,
recv_spot_price: Receiver<spot_price::Response>,
request_quote: Sender<()>,
recv_quote: Receiver<BidQuote>,
} }
impl EventLoopHandle { impl EventLoopHandle {
pub async fn execution_setup(&mut self, state0: State0) -> Result<State2> { pub async fn execution_setup(&mut self, state0: State0) -> Result<State2> {
let _ = self.start_execution_setup.send(state0).await?; self.execution_setup.send_receive(state0).await?
self.done_execution_setup
.recv()
.await
.ok_or_else(|| anyhow!("Failed to setup execution with Alice"))?
} }
pub async fn recv_transfer_proof(&mut self) -> Result<transfer_proof::Request> { pub async fn recv_transfer_proof(&mut self) -> Result<transfer_proof::Request> {
self.recv_transfer_proof let (request, responder) = self
.transfer_proof
.recv() .recv()
.await .await
.ok_or_else(|| anyhow!("Failed to receive transfer proof from Alice")) .context("Failed to receive transfer proof")?;
responder
.respond(())
.context("Failed to acknowledge receipt of transfer proof")?;
Ok(request)
} }
pub async fn request_spot_price(&mut self, btc: bitcoin::Amount) -> Result<monero::Amount> { pub async fn request_spot_price(&mut self, btc: bitcoin::Amount) -> Result<monero::Amount> {
let _ = self Ok(self
.request_spot_price .spot_price
.send(spot_price::Request { btc }) .send_receive(spot_price::Request { btc })
.await?; .await?
.xmr)
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 request_quote(&mut self) -> Result<BidQuote> { pub async fn request_quote(&mut self) -> Result<BidQuote> {
let _ = self.request_quote.send(()).await?; Ok(self.quote.send_receive(()).await?)
let quote = self
.recv_quote
.recv()
.await
.ok_or_else(|| anyhow!("Failed to receive quote from Alice"))?;
Ok(quote)
} }
pub async fn send_encrypted_signature( pub async fn send_encrypted_signature(
&mut self, &mut self,
tx_redeem_encsig: EncryptedSignature, tx_redeem_encsig: EncryptedSignature,
) -> Result<()> { ) -> Result<()> {
self.send_encrypted_signature Ok(self
.send(encrypted_signature::Request { tx_redeem_encsig }) .encrypted_signature
.await?; .send_receive(encrypted_signature::Request { tx_redeem_encsig })
.await?)
Ok(())
}
}
#[derive(Debug)]
struct Channels<T> {
sender: Sender<T>,
receiver: Receiver<T>,
}
impl<T> Channels<T> {
fn new() -> Channels<T> {
let (sender, receiver) = tokio::sync::mpsc::channel(100);
Channels { sender, receiver }
}
}
impl<T> Default for Channels<T> {
fn default() -> Self {
Self::new()
} }
} }

Loading…
Cancel
Save