Merge #265
265: Replace quote with spot-price protocol r=thomaseizinger a=thomaseizinger This is essentially functionally equivalent but includes some cleanups by removing a layer of abstraction: `spot_price::Behaviour` is now just a type-alias for a request-response behaviour. Co-authored-by: Thomas Eizinger <thomas@eizinger.io>pull/274/head
commit
d1363d130c
@ -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<SpotPriceRequest, SpotPriceResponse>;
|
||||
|
||||
/// 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<CborCodec<SpotPriceProtocol, SpotPriceRequest, SpotPriceResponse>>;
|
||||
|
||||
/// 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(),
|
||||
)
|
||||
}
|
@ -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<QuoteResponse>,
|
||||
bob_peer_id: PeerId,
|
||||
},
|
||||
ResponseSent,
|
||||
Failure(Error),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct QuoteResponse {
|
||||
pub xmr_amount: monero::Amount,
|
||||
}
|
||||
|
||||
impl From<RequestResponseEvent<QuoteRequest, QuoteResponse>> for OutEvent {
|
||||
fn from(event: RequestResponseEvent<QuoteRequest, QuoteResponse>) -> 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<CborCodec<Swap, QuoteRequest, QuoteResponse>>,
|
||||
}
|
||||
|
||||
impl Behaviour {
|
||||
/// Alice always sends her messages as a response to a request from Bob.
|
||||
pub fn send(
|
||||
&mut self,
|
||||
channel: ResponseChannel<QuoteResponse>,
|
||||
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,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
@ -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<CborCodec<Swap, QuoteRequest, QuoteResponse>>,
|
||||
}
|
||||
|
||||
impl Behaviour {
|
||||
pub fn send(&mut self, alice: PeerId, quote_request: QuoteRequest) -> Result<RequestId> {
|
||||
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<RequestResponseEvent<QuoteRequest, QuoteResponse>> for OutEvent {
|
||||
fn from(event: RequestResponseEvent<QuoteRequest, QuoteResponse>) -> 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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue