Add resume-only mode for the ASB

Resume-only is a maintenance mode where no swaps are accepted but unfinished swaps are resumed.
This is achieve by ignoring incoming spot-price requests (that would lead to execution setup) in the event-loop.
This commit is contained in:
Daniel Karzel 2021-04-29 19:26:19 +10:00
parent 08d7d587cf
commit f6497778ed
No known key found for this signature in database
GPG Key ID: 30C3FC2E438ADB6E
7 changed files with 58 additions and 6 deletions

View File

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- Resume-only mode for the ASB.
When started with `--resume-only` the ASB does not accept new, incoming swap requests but only finishes swaps that are resumed upon startup.
### Fixed ### Fixed
- An issue where both the ASB and the CLI point to the same default directory `xmr-btc-swap` for storing data. - An issue where both the ASB and the CLI point to the same default directory `xmr-btc-swap` for storing data.

View File

@ -34,6 +34,12 @@ pub enum Command {
default_value = "0.02" default_value = "0.02"
)] )]
ask_spread: Decimal, ask_spread: Decimal,
#[structopt(
long = "resume-only",
help = "For maintenance only. When set, no new swap requests will be accepted, but existing unfinished swaps will be resumed."
)]
resume_only: bool,
}, },
History, History,
WithdrawBtc { WithdrawBtc {

View File

@ -81,6 +81,7 @@ async fn main() -> Result<()> {
Command::Start { Command::Start {
max_buy, max_buy,
ask_spread, ask_spread,
resume_only,
} => { } => {
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
let monero_wallet = init_monero_wallet(&config, env_config).await?; let monero_wallet = init_monero_wallet(&config, env_config).await?;
@ -133,6 +134,7 @@ async fn main() -> Result<()> {
Arc::new(db), Arc::new(db),
KrakenRate::new(ask_spread, kraken_price_updates), KrakenRate::new(ask_spread, kraken_price_updates),
max_buy, max_buy,
resume_only,
) )
.unwrap(); .unwrap();

View File

@ -41,7 +41,16 @@ pub struct Request {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Response { pub struct Response {
pub xmr: monero::Amount, pub xmr: Option<monero::Amount>,
pub error: Option<Error>,
}
#[derive(Clone, Debug, thiserror::Error, Serialize, Deserialize)]
pub enum Error {
#[error(
"This seller currently does not accept incoming swap requests, please try again later"
)]
MaintenanceMode,
} }
/// 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.

View File

@ -42,6 +42,7 @@ pub struct EventLoop<RS> {
max_buy: bitcoin::Amount, max_buy: bitcoin::Amount,
swap_sender: mpsc::Sender<Swap>, swap_sender: mpsc::Sender<Swap>,
resume_only: bool,
/// Stores incoming [`EncryptedSignature`]s per swap. /// Stores incoming [`EncryptedSignature`]s per swap.
recv_encrypted_signature: HashMap<Uuid, bmrng::RequestSender<bitcoin::EncryptedSignature, ()>>, recv_encrypted_signature: HashMap<Uuid, bmrng::RequestSender<bitcoin::EncryptedSignature, ()>>,
@ -62,6 +63,7 @@ impl<LR> EventLoop<LR>
where where
LR: LatestRate, LR: LatestRate,
{ {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
swarm: Swarm<Behaviour>, swarm: Swarm<Behaviour>,
env_config: Config, env_config: Config,
@ -70,6 +72,7 @@ where
db: Arc<Database>, db: Arc<Database>,
latest_rate: LR, latest_rate: LR,
max_buy: bitcoin::Amount, max_buy: bitcoin::Amount,
resume_only: bool,
) -> Result<(Self, mpsc::Receiver<Swap>)> { ) -> Result<(Self, mpsc::Receiver<Swap>)> {
let swap_channel = MpscChannels::default(); let swap_channel = MpscChannels::default();
@ -81,6 +84,7 @@ where
db, db,
latest_rate, latest_rate,
swap_sender: swap_channel.sender, swap_sender: swap_channel.sender,
resume_only,
max_buy, max_buy,
recv_encrypted_signature: Default::default(), recv_encrypted_signature: Default::default(),
inflight_encrypted_signatures: Default::default(), inflight_encrypted_signatures: Default::default(),
@ -144,6 +148,20 @@ 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) => xmr,
@ -153,7 +171,7 @@ where
} }
}; };
match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response { xmr }) { match self.swarm.behaviour_mut().spot_price.send_response(channel, spot_price::Response { xmr: Some(xmr), error: None }) {
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

View File

@ -3,7 +3,7 @@ use crate::network::quote::BidQuote;
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};
use anyhow::{Context, Result}; use anyhow::{bail, Context, Result};
use futures::future::{BoxFuture, OptionFuture}; use futures::future::{BoxFuture, OptionFuture};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use libp2p::request_response::{RequestId, ResponseChannel}; use libp2p::request_response::{RequestId, ResponseChannel};
@ -261,11 +261,22 @@ impl EventLoopHandle {
} }
pub async fn request_spot_price(&mut self, btc: bitcoin::Amount) -> Result<monero::Amount> { pub async fn request_spot_price(&mut self, btc: bitcoin::Amount) -> Result<monero::Amount> {
Ok(self let response = self
.spot_price .spot_price
.send_receive(spot_price::Request { btc }) .send_receive(spot_price::Request { btc })
.await? .await?;
.xmr)
match (response.xmr, response.error) {
(Some(xmr), None) => Ok(xmr),
(_, Some(error)) => {
bail!(error);
}
(None, None) => {
bail!(
"Unexpected response for spot-price request, neither price nor error received"
);
}
}
} }
pub async fn request_quote(&mut self) -> Result<BidQuote> { pub async fn request_quote(&mut self) -> Result<BidQuote> {

View File

@ -234,6 +234,7 @@ fn start_alice(
db, db,
FixedRate::default(), FixedRate::default(),
bitcoin::Amount::ONE_BTC, bitcoin::Amount::ONE_BTC,
false,
) )
.unwrap(); .unwrap();