mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Return proper error to CLI for all expected scenarios
When a CLI requests a spot price have some errors that are expected, where we can provide a proper error message for the CLI: - Balance of ASB too low - Buy amount sent by CLI exceeds maximum buy amount accepted by ASB - ASB is running in maintenance mode and does not accept incoming swap requests All of these errors returns a proper error to the CLI and prints a warning in the ASB logs. Any other unexpected error will result in closing the channel with the CLI and printing an error in the ASB logs.
This commit is contained in:
parent
f6497778ed
commit
ea76ae5821
@ -20,6 +20,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
If you want to access data created by a previous version you will have to rename the data folder or one of the following:
|
If you want to access data created by a previous version you will have to rename the data folder or one of the following:
|
||||||
1. For the CLI you can use `--data-dir` to point to the old directory.
|
1. For the CLI you can use `--data-dir` to point to the old directory.
|
||||||
2. For the ASB you can change the data-dir in the config file of the ASB.
|
2. For the ASB you can change the data-dir in the config file of the ASB.
|
||||||
|
- The CLI receives proper Error messages if setting up a swap with the ASB fails.
|
||||||
|
This is a breaking change because the spot-price protocol response changed.
|
||||||
|
Expected errors scenarios that are now reported back to the CLI:
|
||||||
|
1. Balance of ASB too low
|
||||||
|
2. Buy amount sent by CLI exceeds maximum buy amount accepted by ASB
|
||||||
|
3. ASB is running in resume-only mode and does not accept incoming swap requests
|
||||||
|
|
||||||
## [0.5.0] - 2021-04-17
|
## [0.5.0] - 2021-04-17
|
||||||
|
|
||||||
|
@ -192,12 +192,6 @@ pub struct InsufficientFunds {
|
|||||||
pub actual: Amount,
|
pub actual: Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
|
||||||
#[error("The balance is too low, current balance: {balance}")]
|
|
||||||
pub struct BalanceTooLow {
|
|
||||||
pub balance: Amount,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
||||||
#[error("Overflow, cannot convert {0} to u64")]
|
#[error("Overflow, cannot convert {0} to u64")]
|
||||||
pub struct OverflowError(pub String);
|
pub struct OverflowError(pub String);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
use crate::monero;
|
||||||
use crate::network::cbor_request_response::CborCodec;
|
use crate::network::cbor_request_response::CborCodec;
|
||||||
use crate::protocol::{alice, bob};
|
use crate::protocol::{alice, bob};
|
||||||
use crate::{bitcoin, monero};
|
|
||||||
use libp2p::core::ProtocolName;
|
use libp2p::core::ProtocolName;
|
||||||
use libp2p::request_response::{
|
use libp2p::request_response::{
|
||||||
ProtocolSupport, RequestResponse, RequestResponseConfig, RequestResponseEvent,
|
ProtocolSupport, RequestResponse, RequestResponseConfig, RequestResponseEvent,
|
||||||
@ -40,9 +40,9 @@ pub struct Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Response {
|
pub enum Response {
|
||||||
pub xmr: Option<monero::Amount>,
|
Xmr(monero::Amount),
|
||||||
pub error: Option<Error>,
|
Error(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
|
#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
|
||||||
@ -51,6 +51,18 @@ pub enum Error {
|
|||||||
"This seller currently does not accept incoming swap requests, please try again later"
|
"This seller currently does not accept incoming swap requests, please try again later"
|
||||||
)]
|
)]
|
||||||
MaintenanceMode,
|
MaintenanceMode,
|
||||||
|
#[error("Seller refused to buy {buy} because the maximum configured buy limit is {max}")]
|
||||||
|
MaxBuyAmountExceeded {
|
||||||
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||||
|
max: bitcoin::Amount,
|
||||||
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||||
|
buy: bitcoin::Amount,
|
||||||
|
},
|
||||||
|
#[error("This seller's XMR balance is currently too low to fulfill the swap request to buy {buy}, please try again later")]
|
||||||
|
BalanceTooLow {
|
||||||
|
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||||
|
buy: bitcoin::Amount,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new instance of the `spot-price` behaviour to be used by Alice.
|
/// Constructs a new instance of the `spot-price` behaviour to be used by Alice.
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use crate::asb::Rate;
|
use crate::asb::Rate;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::env::Config;
|
use crate::env::Config;
|
||||||
use crate::monero::BalanceTooLow;
|
|
||||||
use crate::network::quote::BidQuote;
|
use crate::network::quote::BidQuote;
|
||||||
use crate::network::{spot_price, transfer_proof};
|
use crate::network::{spot_price, transfer_proof};
|
||||||
use crate::protocol::alice::{AliceState, Behaviour, OutEvent, State0, State3, Swap};
|
use crate::protocol::alice::{AliceState, Behaviour, OutEvent, State0, State3, Swap};
|
||||||
use crate::{bitcoin, kraken, monero};
|
use crate::{bitcoin, kraken, monero};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use futures::future::{BoxFuture, FutureExt};
|
use futures::future::{BoxFuture, FutureExt};
|
||||||
use futures::stream::{FuturesUnordered, StreamExt};
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
@ -148,30 +147,30 @@ where
|
|||||||
swarm_event = self.swarm.next_event() => {
|
swarm_event = self.swarm.next_event() => {
|
||||||
match swarm_event {
|
match swarm_event {
|
||||||
SwarmEvent::Behaviour(OutEvent::SpotPriceRequested { request, channel, peer }) => {
|
SwarmEvent::Behaviour(OutEvent::SpotPriceRequested { request, channel, peer }) => {
|
||||||
if self.resume_only {
|
|
||||||
tracing::warn!(%peer, "Ignoring spot price request from {} because ASB started in resume-only mode", peer);
|
|
||||||
|
|
||||||
match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response { xmr: None, error: Some(spot_price::Error::MaintenanceMode) }) {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(_) => {
|
|
||||||
tracing::debug!(%peer, "Failed to respond with error to spot price request");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let btc = request.btc;
|
let btc = request.btc;
|
||||||
let xmr = match self.handle_spot_price_request(btc, self.monero_wallet.clone()).await {
|
let xmr = match self.handle_spot_price_request(btc, self.monero_wallet.clone()).await {
|
||||||
Ok(xmr) => xmr,
|
Ok(xmr) => match xmr {
|
||||||
|
Ok(xmr) => xmr,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(%peer, "Ignoring spot price request from {} because: {:#}", peer, e);
|
||||||
|
match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response::Error(e)) {
|
||||||
|
Ok(_) => {
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
tracing::debug!(%peer, "Failed to respond with error to spot price request");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(%peer, "Failed to produce spot price for {}: {:#}", btc, e);
|
tracing::error!(%peer, "Unrecoverable error while producing spot price for {}: {:#}", btc, e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response { xmr: Some(xmr), error: None }) {
|
match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response::Xmr(xmr)) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// if we can't respond, the peer probably just disconnected so it is not a huge deal, only log this on debug
|
// if we can't respond, the peer probably just disconnected so it is not a huge deal, only log this on debug
|
||||||
@ -365,17 +364,21 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
btc: bitcoin::Amount,
|
btc: bitcoin::Amount,
|
||||||
monero_wallet: Arc<monero::Wallet>,
|
monero_wallet: Arc<monero::Wallet>,
|
||||||
) -> Result<monero::Amount> {
|
) -> Result<Result<monero::Amount, spot_price::Error>> {
|
||||||
|
if self.resume_only {
|
||||||
|
return Ok(Err(spot_price::Error::MaintenanceMode));
|
||||||
|
}
|
||||||
|
|
||||||
let rate = self
|
let rate = self
|
||||||
.latest_rate
|
.latest_rate
|
||||||
.latest_rate()
|
.latest_rate()
|
||||||
.context("Failed to get latest rate")?;
|
.context("Failed to get latest rate")?;
|
||||||
|
|
||||||
if btc > self.max_buy {
|
if btc > self.max_buy {
|
||||||
bail!(MaximumBuyAmountExceeded {
|
return Ok(Err(spot_price::Error::MaxBuyAmountExceeded {
|
||||||
actual: btc,
|
buy: btc,
|
||||||
max: self.max_buy
|
max: self.max_buy,
|
||||||
})
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let xmr_balance = monero_wallet.get_balance().await?;
|
let xmr_balance = monero_wallet.get_balance().await?;
|
||||||
@ -383,12 +386,10 @@ where
|
|||||||
let xmr = rate.sell_quote(btc)?;
|
let xmr = rate.sell_quote(btc)?;
|
||||||
|
|
||||||
if xmr_balance < xmr + xmr_lock_fees {
|
if xmr_balance < xmr + xmr_lock_fees {
|
||||||
bail!(BalanceTooLow {
|
return Ok(Err(spot_price::Error::BalanceTooLow { buy: btc }));
|
||||||
balance: xmr_balance
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(xmr)
|
Ok(Ok(xmr))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn make_quote(&mut self, max_buy: bitcoin::Amount) -> Result<BidQuote> {
|
async fn make_quote(&mut self, max_buy: bitcoin::Amount) -> Result<BidQuote> {
|
||||||
@ -569,13 +570,6 @@ impl EventLoopHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
|
||||||
#[error("Refusing to buy {actual} because the maximum configured limit is {max}")]
|
|
||||||
pub struct MaximumBuyAmountExceeded {
|
|
||||||
pub max: bitcoin::Amount,
|
|
||||||
pub actual: bitcoin::Amount,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
struct MpscChannels<T> {
|
struct MpscChannels<T> {
|
||||||
sender: mpsc::Sender<T>,
|
sender: mpsc::Sender<T>,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::bitcoin::EncryptedSignature;
|
use crate::bitcoin::EncryptedSignature;
|
||||||
use crate::network::quote::BidQuote;
|
use crate::network::quote::BidQuote;
|
||||||
|
use crate::network::spot_price::Response;
|
||||||
use crate::network::{encrypted_signature, spot_price};
|
use crate::network::{encrypted_signature, spot_price};
|
||||||
use crate::protocol::bob::{Behaviour, OutEvent, State0, State2};
|
use crate::protocol::bob::{Behaviour, OutEvent, State0, State2};
|
||||||
use crate::{bitcoin, monero};
|
use crate::{bitcoin, monero};
|
||||||
@ -266,16 +267,11 @@ impl EventLoopHandle {
|
|||||||
.send_receive(spot_price::Request { btc })
|
.send_receive(spot_price::Request { btc })
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match (response.xmr, response.error) {
|
match response {
|
||||||
(Some(xmr), None) => Ok(xmr),
|
Response::Xmr(xmr) => Ok(xmr),
|
||||||
(_, Some(error)) => {
|
Response::Error(error) => {
|
||||||
bail!(error);
|
bail!(error);
|
||||||
}
|
}
|
||||||
(None, None) => {
|
|
||||||
bail!(
|
|
||||||
"Unexpected response for spot-price request, neither price nor error received"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user