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(), )