Alice's spot_price Behaviour reports back Error

Instead of handling all errors on the inside spot_price errors are bubbled up (as `SwapRequestDeclined`).
This allows us to test both Alice's and Bob's behaviour for all scenarios.
pull/461/head
Daniel Karzel 3 years ago
parent 5aac76598d
commit 03a0dc73cd
No known key found for this signature in database
GPG Key ID: 30C3FC2E438ADB6E

@ -6,11 +6,14 @@ use crate::protocol::alice::{execution_setup, spot_price, State3};
use anyhow::{anyhow, Error};
use libp2p::request_response::{RequestId, ResponseChannel};
use libp2p::{NetworkBehaviour, PeerId};
use std::fmt::Debug;
use uuid::Uuid;
#[derive(Debug)]
pub enum OutEvent {
SwapRequestDeclined {
peer: PeerId,
error: spot_price::Error,
},
ExecutionSetupStart {
peer: PeerId,
btc: bitcoin::Amount,
@ -65,7 +68,7 @@ impl OutEvent {
#[allow(missing_debug_implementations)]
pub struct Behaviour<LR>
where
LR: LatestRate + Send + 'static + Debug,
LR: LatestRate + Send + 'static,
{
pub quote: quote::Behaviour,
pub spot_price: spot_price::Behaviour<LR>,
@ -76,7 +79,7 @@ where
impl<LR> Behaviour<LR>
where
LR: LatestRate + Send + 'static + Debug,
LR: LatestRate + Send + 'static,
{
pub fn new(
balance: monero::Amount,

@ -3,6 +3,7 @@ use crate::database::Database;
use crate::env::Config;
use crate::network::quote::BidQuote;
use crate::network::transfer_proof;
use crate::protocol::alice::spot_price::Error;
use crate::protocol::alice::{AliceState, Behaviour, OutEvent, State0, State3, Swap};
use crate::{bitcoin, kraken, monero};
use anyhow::{Context, Result};
@ -203,6 +204,18 @@ where
self.swarm.behaviour_mut().execution_setup.run(peer, state0);
}
SwarmEvent::Behaviour(OutEvent::SwapRequestDeclined { peer, error }) => {
match error {
Error::ResumeOnlyMode | Error::MaxBuyAmountExceeded { .. } => {
tracing::warn!(%peer, "Ignoring spot price request because: {}", error);
}
Error::BalanceTooLow { .. }
| Error::LatestRateFetchFailed(_)
| Error::SellQuoteCalculationFailed(_) => {
tracing::error!(%peer, "Ignoring spot price request because: {}", error);
}
}
}
SwarmEvent::Behaviour(OutEvent::QuoteRequested { channel, peer }) => {
// TODO: Move the spot-price update into dedicated update stream to decouple it from quote requests
let current_balance = self.monero_wallet.get_balance().await;

@ -14,10 +14,16 @@ use std::collections::VecDeque;
use std::fmt::Debug;
use std::task::{Context, Poll};
pub struct OutEvent {
peer: PeerId,
btc: bitcoin::Amount,
xmr: monero::Amount,
pub enum OutEvent {
ExecutionSetupParams {
peer: PeerId,
btc: bitcoin::Amount,
xmr: monero::Amount,
},
Error {
peer: PeerId,
error: Error,
},
}
#[derive(NetworkBehaviour)]
@ -25,7 +31,7 @@ pub struct OutEvent {
#[allow(missing_debug_implementations)]
pub struct Behaviour<LR>
where
LR: LatestRate + Send + 'static + Debug,
LR: LatestRate + Send + 'static,
{
behaviour: spot_price::Behaviour,
@ -50,7 +56,7 @@ where
/// bubbled up to the parent behaviour.
impl<LR> Behaviour<LR>
where
LR: LatestRate + Send + 'static + Debug,
LR: LatestRate + Send + 'static,
{
pub fn new(
balance: monero::Amount,
@ -78,30 +84,24 @@ where
self.balance = balance;
}
fn send_error_response(
fn decline(
&mut self,
peer: PeerId,
channel: ResponseChannel<spot_price::Response>,
error: Error<LR>,
error: Error,
) {
match error {
Error::ResumeOnlyMode | Error::MaxBuyAmountExceeded { .. } => {
tracing::warn!(%peer, "Ignoring spot price request because: {}", error);
}
Error::BalanceTooLow { .. }
| Error::LatestRateFetchFailed(_)
| Error::SellQuoteCalculationFailed(_) => {
tracing::error!(%peer, "Ignoring spot price request because: {}", error);
}
}
if self
.behaviour
.send_response(channel, spot_price::Response::Error(error.into()))
.send_response(
channel,
spot_price::Response::Error(error.to_error_response()),
)
.is_err()
{
tracing::debug!(%peer, "Unable to send error response for spot price request");
}
self.events.push_back(OutEvent::Error { peer, error });
}
fn poll<BIE>(
@ -120,7 +120,7 @@ where
impl<LR> NetworkBehaviourEventProcess<spot_price::OutEvent> for Behaviour<LR>
where
LR: LatestRate + Send + 'static + Debug,
LR: LatestRate + Send + 'static,
{
fn inject_event(&mut self, event: spot_price::OutEvent) {
let (peer, message) = match event {
@ -150,13 +150,13 @@ where
};
if self.resume_only {
self.send_error_response(peer, channel, Error::ResumeOnlyMode);
self.decline(peer, channel, Error::ResumeOnlyMode);
return;
}
let btc = request.btc;
if btc > self.max_buy {
self.send_error_response(peer, channel, Error::MaxBuyAmountExceeded {
self.decline(peer, channel, Error::MaxBuyAmountExceeded {
max: self.max_buy,
buy: btc,
});
@ -166,14 +166,14 @@ where
let rate = match self.latest_rate.latest_rate() {
Ok(rate) => rate,
Err(e) => {
self.send_error_response(peer, channel, Error::LatestRateFetchFailed(e));
self.decline(peer, channel, Error::LatestRateFetchFailed(Box::new(e)));
return;
}
};
let xmr = match rate.sell_quote(btc) {
Ok(xmr) => xmr,
Err(e) => {
self.send_error_response(peer, channel, Error::SellQuoteCalculationFailed(e));
self.decline(peer, channel, Error::SellQuoteCalculationFailed(e));
return;
}
};
@ -182,7 +182,7 @@ where
let xmr_lock_fees = self.lock_fee;
if xmr_balance < xmr + xmr_lock_fees {
self.send_error_response(peer, channel, Error::BalanceTooLow { buy: btc });
self.decline(peer, channel, Error::BalanceTooLow { buy: btc });
return;
}
@ -194,43 +194,24 @@ where
tracing::error!(%peer, "Failed to send spot price response of {} for {}", xmr, btc)
}
self.events.push_back(OutEvent { peer, btc, xmr });
self.events
.push_back(OutEvent::ExecutionSetupParams { peer, btc, xmr });
}
}
impl From<OutEvent> for alice::OutEvent {
fn from(event: OutEvent) -> Self {
Self::ExecutionSetupStart {
peer: event.peer,
btc: event.btc,
xmr: event.xmr,
}
}
}
impl<LR> From<Error<LR>> for spot_price::Error
where
LR: LatestRate + Debug,
{
fn from(error: Error<LR>) -> Self {
match error {
Error::ResumeOnlyMode => spot_price::Error::NoSwapsAccepted,
Error::MaxBuyAmountExceeded { max, buy } => {
spot_price::Error::MaxBuyAmountExceeded { max, buy }
}
Error::BalanceTooLow { buy } => spot_price::Error::BalanceTooLow { buy },
Error::LatestRateFetchFailed(_) | Error::SellQuoteCalculationFailed(_) => {
spot_price::Error::Other
match event {
OutEvent::ExecutionSetupParams { peer, btc, xmr } => {
Self::ExecutionSetupStart { peer, btc, xmr }
}
OutEvent::Error { peer, error } => Self::SwapRequestDeclined { peer, error },
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error<LR>
where
LR: LatestRate + Debug,
{
pub enum Error {
#[error("ASB is running in resume-only mode")]
ResumeOnlyMode,
#[error("Maximum buy {max} exceeded {buy}")]
@ -242,8 +223,24 @@ where
BalanceTooLow { buy: bitcoin::Amount },
#[error("Failed to fetch latest rate")]
LatestRateFetchFailed(LR::Error),
LatestRateFetchFailed(#[source] Box<dyn std::error::Error + Send + 'static>),
#[error("Failed to calculate quote: {0}")]
SellQuoteCalculationFailed(anyhow::Error),
SellQuoteCalculationFailed(#[source] anyhow::Error),
}
impl Error {
pub fn to_error_response(&self) -> spot_price::Error {
match self {
Error::ResumeOnlyMode => spot_price::Error::NoSwapsAccepted,
Error::MaxBuyAmountExceeded { max, buy } => spot_price::Error::MaxBuyAmountExceeded {
max: *max,
buy: *buy,
},
Error::BalanceTooLow { buy } => spot_price::Error::BalanceTooLow { buy: *buy },
Error::LatestRateFetchFailed(_) | Error::SellQuoteCalculationFailed(_) => {
spot_price::Error::Other
}
}
}
}

Loading…
Cancel
Save