Replace quote with spot-price protocol
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.pull/265/head
parent
2440964385
commit
7042ed9441
@ -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