From c1e993deb2ee01eb34485a778dc41e433648fe50 Mon Sep 17 00:00:00 2001 From: Daniel Karzel Date: Thu, 15 Apr 2021 21:09:48 +1000 Subject: [PATCH] Json codec for quote protocol A `RequestResponseCodec` for pull-based protocols where the response is encoded using JSON. This was added to more properly express the behavior of the quote protocol, where the dialer doesn't send any message and expects the listener to directly send the response. Co-authored-by: Thomas Eizinger --- CHANGELOG.md | 2 + swap/src/network.rs | 1 + swap/src/network/json_pull_codec.rs | 99 +++++++++++++++++++++++++++++ swap/src/network/quote.rs | 8 +-- 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 swap/src/network/json_pull_codec.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4185b8fa..ad64451d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow multiple concurrent swaps with the same peer on the ASB. This is a breaking change because the swap ID is now agreed upon between CLI and ASB during swap setup. Resuming swaps started prior to this change can result in unexpected behaviour. +- Quote protocol returns JSON encoded data instead of CBOR. + This is a breaking change in the protocol handling, old CLI versions will not be able to process quote requests of ASBs running this version. ### Added diff --git a/swap/src/network.rs b/swap/src/network.rs index bd658573..211445c3 100644 --- a/swap/src/network.rs +++ b/swap/src/network.rs @@ -2,6 +2,7 @@ mod impl_from_rr_event; pub mod cbor_request_response; pub mod encrypted_signature; +pub mod json_pull_codec; pub mod quote; pub mod redial; pub mod spot_price; diff --git a/swap/src/network/json_pull_codec.rs b/swap/src/network/json_pull_codec.rs new file mode 100644 index 00000000..8d802c16 --- /dev/null +++ b/swap/src/network/json_pull_codec.rs @@ -0,0 +1,99 @@ +use async_trait::async_trait; +use futures::prelude::*; +use libp2p::core::upgrade; +use libp2p::request_response::{ProtocolName, RequestResponseCodec}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::fmt::Debug; +use std::io; +use std::marker::PhantomData; + +/// Message receive buffer. +pub const BUF_SIZE: usize = 1024 * 1024; + +/// A [`RequestResponseCodec`] for pull-based protocols where the response is +/// encoded using JSON. +/// +/// A pull-based protocol is a protocol where the dialer doesn't send any +/// message and expects the listener to directly send the response as the +/// substream is opened. +#[derive(Clone, Copy, Debug)] +pub struct JsonPullCodec { + phantom: PhantomData<(P, Res)>, +} + +impl Default for JsonPullCodec { + fn default() -> Self { + Self { + phantom: PhantomData::default(), + } + } +} + +#[async_trait] +impl RequestResponseCodec for JsonPullCodec +where + P: ProtocolName + Send + Sync + Clone, + Res: DeserializeOwned + Serialize + Send, +{ + type Protocol = P; + type Request = (); + type Response = Res; + + async fn read_request(&mut self, _: &Self::Protocol, _: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + Ok(()) + } + + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let message = upgrade::read_one(io, BUF_SIZE) + .await + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let mut de = serde_json::Deserializer::from_slice(&message); + let msg = Res::deserialize(&mut de).map_err(|e| { + tracing::debug!("serde read_response error: {:?}", e); + io::Error::new(io::ErrorKind::InvalidData, e) + })?; + + Ok(msg) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + _: &mut T, + _: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let bytes = serde_json::to_vec(&res).map_err(|e| { + tracing::debug!("serde write_response error: {:?}", e); + io::Error::new(io::ErrorKind::InvalidData, e) + })?; + upgrade::write_one(io, &bytes).await?; + + Ok(()) + } +} diff --git a/swap/src/network/quote.rs b/swap/src/network/quote.rs index 1efa91e3..1920ae0e 100644 --- a/swap/src/network/quote.rs +++ b/swap/src/network/quote.rs @@ -1,5 +1,5 @@ use crate::bitcoin; -use crate::network::cbor_request_response::CborCodec; +use crate::network::json_pull_codec::JsonPullCodec; use crate::protocol::{alice, bob}; use libp2p::core::ProtocolName; use libp2p::request_response::{ @@ -13,7 +13,7 @@ const PROTOCOL: &str = "/comit/xmr/btc/bid-quote/1.0.0"; type OutEvent = RequestResponseEvent<(), BidQuote>; type Message = RequestResponseMessage<(), BidQuote>; -pub type Behaviour = RequestResponse>; +pub type Behaviour = RequestResponse>; #[derive(Debug, Clone, Copy, Default)] pub struct BidQuoteProtocol; @@ -40,7 +40,7 @@ pub struct BidQuote { /// Alice only supports inbound connections, i.e. handing out quotes. pub fn alice() -> Behaviour { Behaviour::new( - CborCodec::default(), + JsonPullCodec::default(), vec![(BidQuoteProtocol, ProtocolSupport::Inbound)], RequestResponseConfig::default(), ) @@ -51,7 +51,7 @@ pub fn alice() -> Behaviour { /// Bob only supports outbound connections, i.e. requesting quotes. pub fn bob() -> Behaviour { Behaviour::new( - CborCodec::default(), + JsonPullCodec::default(), vec![(BidQuoteProtocol, ProtocolSupport::Outbound)], RequestResponseConfig::default(), )