mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-09 06:52:53 -04:00
WIP: Struct for concurrent swaps manager
This commit is contained in:
parent
ec65ea2b27
commit
bbcfffab6d
12 changed files with 411 additions and 484 deletions
|
@ -7,12 +7,12 @@ use crate::network::rendezvous::XmrBtcNamespace;
|
||||||
use crate::protocol::Database;
|
use crate::protocol::Database;
|
||||||
use crate::seed::Seed;
|
use crate::seed::Seed;
|
||||||
use crate::{bitcoin, cli, monero};
|
use crate::{bitcoin, cli, monero};
|
||||||
use anyhow::{Context as AnyContext, Result};
|
use anyhow::{bail, Context as AnyContext, Error, Result};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Once};
|
use std::sync::{Arc, Once};
|
||||||
use tokio::sync::{broadcast, Mutex, broadcast::Receiver, broadcast::Sender};
|
use tokio::sync::{broadcast, broadcast::Receiver, broadcast::Sender, RwLock};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
static START: Once = Once::new();
|
static START: Once = Once::new();
|
||||||
|
@ -30,43 +30,72 @@ pub struct Config {
|
||||||
pub is_testnet: bool,
|
pub is_testnet: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shutdown {
|
use uuid::Uuid;
|
||||||
pub fn new(listen: Receiver<()>) -> Shutdown {
|
|
||||||
let (notify, _) = broadcast::channel(16);
|
pub struct SwapLock {
|
||||||
Shutdown {
|
current_swap: RwLock<Option<Uuid>>,
|
||||||
shutdown: Mutex::new(false),
|
_suspension_rec: Receiver<()>,
|
||||||
listen: Mutex::new(listen),
|
suspension_trigger: Sender<()>,
|
||||||
notify
|
}
|
||||||
|
|
||||||
|
impl SwapLock {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (suspension_trigger, _suspension_rec) = broadcast::channel(10);
|
||||||
|
SwapLock {
|
||||||
|
current_swap: RwLock::new(None),
|
||||||
|
_suspension_rec,
|
||||||
|
suspension_trigger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive the shutdown notice, waiting if necessary.
|
pub async fn listen_for_swap_force_suspension(&self) -> Result<(), Error> {
|
||||||
pub async fn recv(&self) {
|
let mut listener = self.suspension_trigger.subscribe();
|
||||||
// If the shutdown signal has already been received, then return
|
let event = listener.recv().await;
|
||||||
// immediately.
|
match event {
|
||||||
let mut guard_shutdown = self.shutdown.lock().await;
|
Ok(_) => Ok(()),
|
||||||
if *guard_shutdown {
|
Err(e) => {
|
||||||
return;
|
tracing::error!("Error receiving swap suspension signal: {}", e);
|
||||||
|
bail!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn acquire_swap_lock(&self, swap_id: Uuid) -> Result<(), Error> {
|
||||||
|
let mut current_swap = self.current_swap.write().await;
|
||||||
|
if current_swap.is_some() {
|
||||||
|
bail!("There already exists an active swap lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.listen.lock().await.recv().await;
|
tracing::debug!(swap_id = %swap_id, "Acquiring swap lock");
|
||||||
|
*current_swap = Some(swap_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Remember that the signal has been received.
|
pub async fn get_current_swap_id(&self) -> Option<Uuid> {
|
||||||
*guard_shutdown = true;
|
let current_swap = self.current_swap.read().await.clone();
|
||||||
|
current_swap
|
||||||
|
}
|
||||||
|
|
||||||
// Send shutdown request to child tasks
|
pub async fn send_suspend_signal(&self) -> Result<(), Error> {
|
||||||
|
let _ = self.suspension_trigger.send(())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn release_swap_lock(&self) -> Result<Uuid, Error> {
|
||||||
|
let mut current_swap = self.current_swap.write().await;
|
||||||
|
if let Some(swap_id) = current_swap.as_ref() {
|
||||||
|
tracing::debug!(swap_id = %swap_id, "Releasing swap lock");
|
||||||
|
|
||||||
|
let prev_swap_id = swap_id.clone();
|
||||||
|
*current_swap = None;
|
||||||
|
drop(current_swap);
|
||||||
|
Ok(prev_swap_id)
|
||||||
|
} else {
|
||||||
|
bail!("There is no current swap lock to release");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Shutdown {
|
|
||||||
shutdown: Mutex<bool>,
|
|
||||||
listen: Mutex<Receiver<()>>,
|
|
||||||
notify: Sender<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// workaround for warning over monero_rpc_process which we must own but not read
|
// workaround for warning over monero_rpc_process which we must own but not read
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -74,8 +103,8 @@ pub struct Context {
|
||||||
bitcoin_wallet: Option<Arc<bitcoin::Wallet>>,
|
bitcoin_wallet: Option<Arc<bitcoin::Wallet>>,
|
||||||
monero_wallet: Option<Arc<monero::Wallet>>,
|
monero_wallet: Option<Arc<monero::Wallet>>,
|
||||||
monero_rpc_process: Option<monero::WalletRpcProcess>,
|
monero_rpc_process: Option<monero::WalletRpcProcess>,
|
||||||
|
swap_lock: Arc<SwapLock>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub shutdown: Arc<Shutdown>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
@ -88,7 +117,6 @@ impl Context {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
json: bool,
|
json: bool,
|
||||||
server_address: Option<SocketAddr>,
|
server_address: Option<SocketAddr>,
|
||||||
sender: broadcast::Sender<()>,
|
|
||||||
) -> Result<Context> {
|
) -> Result<Context> {
|
||||||
let data_dir = data::data_dir_from(data, is_testnet)?;
|
let data_dir = data::data_dir_from(data, is_testnet)?;
|
||||||
let env_config = env_config_from(is_testnet);
|
let env_config = env_config_from(is_testnet);
|
||||||
|
@ -148,7 +176,7 @@ impl Context {
|
||||||
is_testnet,
|
is_testnet,
|
||||||
data_dir,
|
data_dir,
|
||||||
},
|
},
|
||||||
shutdown: Arc::new(Shutdown::new(sender.subscribe())),
|
swap_lock: Arc::new(SwapLock::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(context)
|
Ok(context)
|
||||||
|
@ -239,7 +267,7 @@ fn env_config_from(testnet: bool) -> EnvConfig {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod api_test {
|
pub mod api_test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::api::request::{Method, Params, Request};
|
use crate::api::request::{Method, Request};
|
||||||
|
|
||||||
use libp2p::Multiaddr;
|
use libp2p::Multiaddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
|
@ -19,21 +19,14 @@ use std::future::Future;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use structopt::lazy_static::lazy_static;
|
|
||||||
use tracing::{debug_span, Instrument};
|
use tracing::{debug_span, Instrument};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref SWAP_LOCK: RwLock<Option<Uuid>> = RwLock::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub cmd: Method
|
pub cmd: Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
BuyXmr {
|
BuyXmr {
|
||||||
|
@ -70,26 +63,25 @@ pub enum Method {
|
||||||
GetSwapInfo {
|
GetSwapInfo {
|
||||||
swap_id: Uuid,
|
swap_id: Uuid,
|
||||||
},
|
},
|
||||||
|
SuspendCurrentSwap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
pub fn new(cmd: Method) -> Request {
|
pub fn new(cmd: Method) -> Request {
|
||||||
Request {
|
Request { cmd }
|
||||||
cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_lockable_swap_id(&self) -> Option<Uuid> {
|
|
||||||
match self.cmd {
|
|
||||||
Method::BuyXmr { swap_id, .. }
|
|
||||||
| Method::Resume { swap_id }
|
|
||||||
| Method::CancelAndRefund { swap_id } => Some(swap_id),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_cmd(self, context: Arc<Context>) -> Result<serde_json::Value> {
|
async fn handle_cmd(self, context: Arc<Context>) -> Result<serde_json::Value> {
|
||||||
match self.cmd {
|
match self.cmd {
|
||||||
|
Method::SuspendCurrentSwap => {
|
||||||
|
context.swap_lock.send_suspend_signal().await?;
|
||||||
|
let swap_id = context.swap_lock.get_current_swap_id().await;
|
||||||
|
|
||||||
|
Ok(json!({
|
||||||
|
"swapId": swap_id,
|
||||||
|
"success": true
|
||||||
|
}))
|
||||||
|
}
|
||||||
Method::GetSwapInfo { swap_id } => {
|
Method::GetSwapInfo { swap_id } => {
|
||||||
let bitcoin_wallet = context
|
let bitcoin_wallet = context
|
||||||
.bitcoin_wallet
|
.bitcoin_wallet
|
||||||
|
@ -157,125 +149,139 @@ impl Request {
|
||||||
monero_receive_address,
|
monero_receive_address,
|
||||||
swap_id,
|
swap_id,
|
||||||
} => {
|
} => {
|
||||||
let seed = context.config.seed.as_ref().context("Could not get seed")?;
|
context.swap_lock.acquire_swap_lock(swap_id).await?;
|
||||||
let env_config = context.config.env_config;
|
|
||||||
let btc = context
|
|
||||||
.bitcoin_wallet
|
|
||||||
.as_ref()
|
|
||||||
.context("Could not get Bitcoin wallet")?;
|
|
||||||
|
|
||||||
let bitcoin_wallet = btc;
|
|
||||||
let seller_peer_id = seller
|
|
||||||
.extract_peer_id()
|
|
||||||
.context("Seller address must contain peer ID")?;
|
|
||||||
context
|
|
||||||
.db
|
|
||||||
.insert_address(seller_peer_id, seller.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let behaviour = cli::Behaviour::new(
|
|
||||||
seller_peer_id,
|
|
||||||
env_config,
|
|
||||||
bitcoin_wallet.clone(),
|
|
||||||
(seed.derive_libp2p_identity(), context.config.namespace),
|
|
||||||
);
|
|
||||||
let mut swarm = swarm::cli(
|
|
||||||
seed.derive_libp2p_identity(),
|
|
||||||
context
|
|
||||||
.config
|
|
||||||
.tor_socks5_port
|
|
||||||
.context("Could not get Tor SOCKS5 port")?,
|
|
||||||
behaviour,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
swarm.behaviour_mut().add_address(seller_peer_id, seller);
|
|
||||||
|
|
||||||
tracing::debug!(peer_id = %swarm.local_peer_id(), "Network layer initialized");
|
|
||||||
|
|
||||||
let (event_loop, mut event_loop_handle) =
|
|
||||||
EventLoop::new(swap_id, swarm, seller_peer_id)?;
|
|
||||||
let event_loop = tokio::spawn(event_loop.run());
|
|
||||||
|
|
||||||
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
|
|
||||||
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
|
|
||||||
|
|
||||||
let (amount, fees) = match determine_btc_to_swap(
|
|
||||||
context.config.json,
|
|
||||||
event_loop_handle.request_quote(),
|
|
||||||
bitcoin_wallet.new_address(),
|
|
||||||
|| bitcoin_wallet.balance(),
|
|
||||||
max_givable,
|
|
||||||
|| bitcoin_wallet.sync(),
|
|
||||||
estimate_fee,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(error) => match error.downcast::<ZeroQuoteReceived>() {
|
|
||||||
Ok(_) => {
|
|
||||||
bail!("Seller's XMR balance is currently too low to initiate a swap, please try again later")
|
|
||||||
}
|
|
||||||
Err(other) => bail!(other),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing::info!(%amount, %fees, "Determined swap amount");
|
|
||||||
|
|
||||||
context.db.insert_peer_id(swap_id, seller_peer_id).await?;
|
|
||||||
|
|
||||||
context
|
|
||||||
.db
|
|
||||||
.insert_monero_address(swap_id, monero_receive_address)
|
|
||||||
.await?;
|
|
||||||
let monero_wallet = context
|
|
||||||
.monero_wallet
|
|
||||||
.as_ref()
|
|
||||||
.context("Could not get Monero wallet")?;
|
|
||||||
|
|
||||||
let swap = Swap::new(
|
|
||||||
Arc::clone(&context.db),
|
|
||||||
swap_id,
|
|
||||||
Arc::clone(bitcoin_wallet),
|
|
||||||
Arc::clone(monero_wallet),
|
|
||||||
env_config,
|
|
||||||
event_loop_handle,
|
|
||||||
monero_receive_address,
|
|
||||||
bitcoin_change_address,
|
|
||||||
amount,
|
|
||||||
);
|
|
||||||
let mut halt = context.shutdown.notify.subscribe();
|
|
||||||
|
|
||||||
// execution will halt if the server daemon is stopped or a cancel running swap
|
|
||||||
// request is sent
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
result = event_loop => {
|
biased;
|
||||||
match result {
|
_ = context.swap_lock.listen_for_swap_force_suspension() => {
|
||||||
Ok(_) => {
|
tracing::info!("Shutdown signal received, exiting");
|
||||||
tracing::debug!(%swap_id, "EventLoop completed")
|
()
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
tracing::error!(%swap_id, "EventLoop failed: {:#}", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
result = bob::run(swap) => {
|
_ = async {
|
||||||
match result {
|
let seed = context.config.seed.as_ref().context("Could not get seed")?;
|
||||||
Ok(state) => {
|
let env_config = context.config.env_config;
|
||||||
tracing::debug!(%swap_id, state=%state, "Swap completed")
|
let bitcoin_wallet = context
|
||||||
}
|
.bitcoin_wallet
|
||||||
Err(error) => {
|
.as_ref()
|
||||||
tracing::error!(%swap_id, "Failed to complete swap: {:#}", error)
|
.context("Could not get Bitcoin wallet")?;
|
||||||
}
|
|
||||||
|
let seller_peer_id = seller
|
||||||
|
.extract_peer_id()
|
||||||
|
.context("Seller address must contain peer ID")?;
|
||||||
|
context
|
||||||
|
.db
|
||||||
|
.insert_address(seller_peer_id, seller.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let behaviour = cli::Behaviour::new(
|
||||||
|
seller_peer_id,
|
||||||
|
env_config,
|
||||||
|
bitcoin_wallet.clone(),
|
||||||
|
(seed.derive_libp2p_identity(), context.config.namespace),
|
||||||
|
);
|
||||||
|
let mut swarm = swarm::cli(
|
||||||
|
seed.derive_libp2p_identity(),
|
||||||
|
context
|
||||||
|
.config
|
||||||
|
.tor_socks5_port
|
||||||
|
.context("Could not get Tor SOCKS5 port")?,
|
||||||
|
behaviour,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
swarm.behaviour_mut().add_address(seller_peer_id, seller);
|
||||||
|
|
||||||
|
tracing::debug!(peer_id = %swarm.local_peer_id(), "Network layer initialized");
|
||||||
|
|
||||||
|
let (event_loop, mut event_loop_handle) =
|
||||||
|
EventLoop::new(swap_id, swarm, seller_peer_id)?;
|
||||||
|
let event_loop = tokio::spawn(event_loop.run());
|
||||||
|
|
||||||
|
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
|
||||||
|
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
|
||||||
|
|
||||||
|
let (amount, fees) = match determine_btc_to_swap(
|
||||||
|
context.config.json,
|
||||||
|
event_loop_handle.request_quote(),
|
||||||
|
bitcoin_wallet.new_address(),
|
||||||
|
|| bitcoin_wallet.balance(),
|
||||||
|
max_givable,
|
||||||
|
|| bitcoin_wallet.sync(),
|
||||||
|
estimate_fee,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(error) => match error.downcast::<ZeroQuoteReceived>() {
|
||||||
|
Ok(_) => {
|
||||||
|
bail!("Seller's XMR balance is currently too low to initiate a swap, please try again later")
|
||||||
|
}
|
||||||
|
Err(other) => bail!(other),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!(%amount, %fees, "Determined swap amount");
|
||||||
|
|
||||||
|
context.db.insert_peer_id(swap_id, seller_peer_id).await?;
|
||||||
|
|
||||||
|
context
|
||||||
|
.db
|
||||||
|
.insert_monero_address(swap_id, monero_receive_address)
|
||||||
|
.await?;
|
||||||
|
let monero_wallet = context
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.context("Could not get Monero wallet")?;
|
||||||
|
|
||||||
|
let swap = Swap::new(
|
||||||
|
Arc::clone(&context.db),
|
||||||
|
swap_id,
|
||||||
|
Arc::clone(bitcoin_wallet),
|
||||||
|
Arc::clone(monero_wallet),
|
||||||
|
env_config,
|
||||||
|
event_loop_handle,
|
||||||
|
monero_receive_address,
|
||||||
|
bitcoin_change_address,
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
result = event_loop => {
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
tracing::debug!(%swap_id, "EventLoop completed")
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%swap_id, "EventLoop failed: {:#}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result = bob::run(swap) => {
|
||||||
|
match result {
|
||||||
|
Ok(state) => {
|
||||||
|
tracing::debug!(%swap_id, state=%state, "Swap completed")
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(%swap_id, "Failed to complete swap: {:#}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
tracing::debug!(%swap_id, "Swap completed");
|
||||||
|
Ok(())
|
||||||
|
} => {
|
||||||
|
()
|
||||||
}
|
}
|
||||||
_ = halt.recv() => {
|
};
|
||||||
tracing::debug!(%swap_id, "Swap cancel signal received while running swap")
|
context
|
||||||
}
|
.swap_lock
|
||||||
}
|
.release_swap_lock()
|
||||||
|
.await
|
||||||
|
.expect("Could not release swap lock");
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"empty": "true"
|
"swapId": swap_id.to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Method::History => {
|
Method::History => {
|
||||||
|
@ -343,21 +349,16 @@ impl Request {
|
||||||
// Default to 127.0.0.1:1234
|
// Default to 127.0.0.1:1234
|
||||||
let server_address = server_address.unwrap_or("127.0.0.1:1234".parse().unwrap());
|
let server_address = server_address.unwrap_or("127.0.0.1:1234".parse().unwrap());
|
||||||
|
|
||||||
let (_, server_handle) =
|
let (addr, server_handle) =
|
||||||
rpc::run_server(server_address, Arc::clone(&context)).await?;
|
rpc::run_server(server_address, Arc::clone(&context)).await?;
|
||||||
|
|
||||||
loop {
|
tracing::info!(%addr, "Started RPC server");
|
||||||
let shutdown = Arc::clone(&context.shutdown);
|
|
||||||
tokio::select! {
|
server_handle.stopped().await;
|
||||||
_ = shutdown.recv() => {
|
|
||||||
server_handle.stop()?;
|
tracing::info!("Server RPC server");
|
||||||
context.shutdown.notify.send(())?;
|
|
||||||
return Ok(json!({
|
Ok(json!({}))
|
||||||
"result": []
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Method::Balance => {
|
Method::Balance => {
|
||||||
let bitcoin_wallet = context
|
let bitcoin_wallet = context
|
||||||
|
@ -376,101 +377,131 @@ impl Request {
|
||||||
"balance": bitcoin_balance.to_sat()
|
"balance": bitcoin_balance.to_sat()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Method::Resume {swap_id} => {
|
Method::Resume { swap_id } => {
|
||||||
let seller_peer_id = context.db.get_peer_id(swap_id).await?;
|
context.swap_lock.acquire_swap_lock(swap_id).await?;
|
||||||
let seller_addresses = context.db.get_addresses(seller_peer_id).await?;
|
|
||||||
|
|
||||||
let seed = context
|
tokio::spawn(async move {
|
||||||
.config
|
tokio::select! {
|
||||||
.seed
|
_ = async {
|
||||||
.as_ref()
|
let seller_peer_id = context.db.get_peer_id(swap_id).await?;
|
||||||
.context("Could not get seed")?
|
let seller_addresses = context.db.get_addresses(seller_peer_id).await?;
|
||||||
.derive_libp2p_identity();
|
|
||||||
|
|
||||||
let behaviour = cli::Behaviour::new(
|
let seed = context
|
||||||
seller_peer_id,
|
.config
|
||||||
context.config.env_config,
|
.seed
|
||||||
Arc::clone(
|
.as_ref()
|
||||||
context
|
.context("Could not get seed")?
|
||||||
.bitcoin_wallet
|
.derive_libp2p_identity();
|
||||||
.as_ref()
|
|
||||||
.context("Could not get Bitcoin wallet")?,
|
|
||||||
),
|
|
||||||
(seed.clone(), context.config.namespace),
|
|
||||||
);
|
|
||||||
let mut swarm = swarm::cli(
|
|
||||||
seed.clone(),
|
|
||||||
context
|
|
||||||
.config
|
|
||||||
.tor_socks5_port
|
|
||||||
.context("Could not get Tor SOCKS5 port")?,
|
|
||||||
behaviour,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let our_peer_id = swarm.local_peer_id();
|
|
||||||
|
|
||||||
tracing::debug!(peer_id = %our_peer_id, "Network layer initialized");
|
let behaviour = cli::Behaviour::new(
|
||||||
|
seller_peer_id,
|
||||||
|
context.config.env_config,
|
||||||
|
Arc::clone(
|
||||||
|
context
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.context("Could not get Bitcoin wallet")?,
|
||||||
|
),
|
||||||
|
(seed.clone(), context.config.namespace),
|
||||||
|
);
|
||||||
|
let mut swarm = swarm::cli(
|
||||||
|
seed.clone(),
|
||||||
|
context
|
||||||
|
.config
|
||||||
|
.tor_socks5_port
|
||||||
|
.context("Could not get Tor SOCKS5 port")?,
|
||||||
|
behaviour,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let our_peer_id = swarm.local_peer_id();
|
||||||
|
|
||||||
for seller_address in seller_addresses {
|
tracing::debug!(peer_id = %our_peer_id, "Network layer initialized");
|
||||||
swarm
|
|
||||||
.behaviour_mut()
|
|
||||||
.add_address(seller_peer_id, seller_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (event_loop, event_loop_handle) =
|
for seller_address in seller_addresses {
|
||||||
EventLoop::new(swap_id, swarm, seller_peer_id)?;
|
swarm
|
||||||
let handle = tokio::spawn(event_loop.run());
|
.behaviour_mut()
|
||||||
|
.add_address(seller_peer_id, seller_address);
|
||||||
|
}
|
||||||
|
|
||||||
let monero_receive_address = context.db.get_monero_address(swap_id).await?;
|
let (event_loop, event_loop_handle) =
|
||||||
let swap = Swap::from_db(
|
EventLoop::new(swap_id, swarm, seller_peer_id)?;
|
||||||
Arc::clone(&context.db),
|
let handle = tokio::spawn(event_loop.run());
|
||||||
swap_id,
|
|
||||||
Arc::clone(
|
|
||||||
context
|
|
||||||
.bitcoin_wallet
|
|
||||||
.as_ref()
|
|
||||||
.context("Could not get Bitcoin wallet")?,
|
|
||||||
),
|
|
||||||
Arc::clone(
|
|
||||||
context
|
|
||||||
.monero_wallet
|
|
||||||
.as_ref()
|
|
||||||
.context("Could not get Monero wallet")?,
|
|
||||||
),
|
|
||||||
context.config.env_config,
|
|
||||||
event_loop_handle,
|
|
||||||
monero_receive_address,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tokio::select! {
|
let monero_receive_address = context.db.get_monero_address(swap_id).await?;
|
||||||
event_loop_result = handle => {
|
let swap = Swap::from_db(
|
||||||
event_loop_result?;
|
Arc::clone(&context.db),
|
||||||
},
|
swap_id,
|
||||||
swap_result = bob::run(swap) => {
|
Arc::clone(
|
||||||
swap_result?;
|
context
|
||||||
|
.bitcoin_wallet
|
||||||
|
.as_ref()
|
||||||
|
.context("Could not get Bitcoin wallet")?,
|
||||||
|
),
|
||||||
|
Arc::clone(
|
||||||
|
context
|
||||||
|
.monero_wallet
|
||||||
|
.as_ref()
|
||||||
|
.context("Could not get Monero wallet")?,
|
||||||
|
),
|
||||||
|
context.config.env_config,
|
||||||
|
event_loop_handle,
|
||||||
|
monero_receive_address,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
event_loop_result = handle => {
|
||||||
|
event_loop_result?;
|
||||||
|
},
|
||||||
|
swap_result = bob::run(swap) => {
|
||||||
|
swap_result?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok::<(), anyhow::Error>(())
|
||||||
|
} => {
|
||||||
|
()
|
||||||
|
},
|
||||||
|
_ = context.swap_lock.listen_for_swap_force_suspension() => {
|
||||||
|
tracing::info!("Shutdown signal received, exiting");
|
||||||
|
()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
context
|
||||||
|
.swap_lock
|
||||||
|
.release_swap_lock()
|
||||||
|
.await
|
||||||
|
.expect("Could not release swap lock");
|
||||||
|
});
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"result": []
|
"result": "ok",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Method::CancelAndRefund {swap_id} => {
|
Method::CancelAndRefund { swap_id } => {
|
||||||
let bitcoin_wallet = context
|
let bitcoin_wallet = context
|
||||||
.bitcoin_wallet
|
.bitcoin_wallet
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.context("Could not get Bitcoin wallet")?;
|
.context("Could not get Bitcoin wallet")?;
|
||||||
|
|
||||||
|
context.swap_lock.acquire_swap_lock(swap_id).await?;
|
||||||
|
|
||||||
let state = cli::cancel_and_refund(
|
let state = cli::cancel_and_refund(
|
||||||
swap_id,
|
swap_id,
|
||||||
Arc::clone(bitcoin_wallet),
|
Arc::clone(bitcoin_wallet),
|
||||||
Arc::clone(&context.db),
|
Arc::clone(&context.db),
|
||||||
)
|
)
|
||||||
.await?;
|
.await;
|
||||||
|
|
||||||
Ok(json!({
|
context
|
||||||
"result": state,
|
.swap_lock
|
||||||
}))
|
.release_swap_lock()
|
||||||
|
.await
|
||||||
|
.expect("Could not release swap lock");
|
||||||
|
|
||||||
|
state.map(|state| {
|
||||||
|
json!({
|
||||||
|
"result": state,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Method::ListSellers { rendezvous_point } => {
|
Method::ListSellers { rendezvous_point } => {
|
||||||
let rendezvous_node_peer_id = rendezvous_point
|
let rendezvous_node_peer_id = rendezvous_point
|
||||||
|
@ -494,7 +525,7 @@ impl Request {
|
||||||
.context("Could not get Tor SOCKS5 port")?,
|
.context("Could not get Tor SOCKS5 port")?,
|
||||||
identity,
|
identity,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for seller in &sellers {
|
for seller in &sellers {
|
||||||
match seller.status {
|
match seller.status {
|
||||||
|
@ -571,7 +602,7 @@ impl Request {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Method::GetCurrentSwap => Ok(json!({
|
Method::GetCurrentSwap => Ok(json!({
|
||||||
"swap_id": SWAP_LOCK.read().await.clone()
|
"swap_id": context.swap_lock.get_current_swap_id().await
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,23 +614,6 @@ impl Request {
|
||||||
method = ?self.cmd,
|
method = ?self.cmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(swap_id) = self.has_lockable_swap_id() {
|
|
||||||
println!("taking lock for swap_id: {}", swap_id);
|
|
||||||
let mut guard = SWAP_LOCK.write().await;
|
|
||||||
if let Some(running_swap_id) = guard.as_ref() {
|
|
||||||
bail!("Another swap is already running: {}", running_swap_id);
|
|
||||||
}
|
|
||||||
let _ = guard.insert(swap_id.clone());
|
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
let result = self.handle_cmd(context).instrument(call_span).await;
|
|
||||||
|
|
||||||
SWAP_LOCK.write().await.take();
|
|
||||||
|
|
||||||
println!("releasing lock for swap_id: {}", swap_id);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
self.handle_cmd(context).instrument(call_span).await
|
self.handle_cmd(context).instrument(call_span).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use tokio::sync::broadcast;
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let (context, mut request) = match parse_args_and_apply_defaults(env::args_os(), tx.clone()).await? {
|
let (context, request) = match parse_args_and_apply_defaults(env::args_os()).await? {
|
||||||
ParseResult::Context(context, request) => (context, request),
|
ParseResult::Context(context, request) => (context, request),
|
||||||
ParseResult::PrintAndExitZero { message } => {
|
ParseResult::PrintAndExitZero { message } => {
|
||||||
println!("{}", message);
|
println!("{}", message);
|
||||||
|
|
|
@ -246,7 +246,7 @@ pub fn current_epoch(
|
||||||
if tx_lock_status.is_confirmed_with(cancel_timelock) {
|
if tx_lock_status.is_confirmed_with(cancel_timelock) {
|
||||||
return ExpiredTimelocks::Cancel {
|
return ExpiredTimelocks::Cancel {
|
||||||
blocks_left: tx_cancel_status.blocks_left_until(punish_timelock),
|
blocks_left: tx_cancel_status.blocks_left_until(punish_timelock),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpiredTimelocks::None {
|
ExpiredTimelocks::None {
|
||||||
|
|
|
@ -39,11 +39,7 @@ impl Add<u32> for BlockHeight {
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ExpiredTimelocks {
|
pub enum ExpiredTimelocks {
|
||||||
None {
|
None { blocks_left: u32 },
|
||||||
blocks_left: u32,
|
Cancel { blocks_left: u32 },
|
||||||
},
|
|
||||||
Cancel {
|
|
||||||
blocks_left: u32,
|
|
||||||
},
|
|
||||||
Punish,
|
Punish,
|
||||||
}
|
}
|
||||||
|
|
|
@ -926,13 +926,16 @@ impl Confirmed {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn meets_target<T>(&self, target: T) -> bool
|
pub fn meets_target<T>(&self, target: T) -> bool
|
||||||
where T: Into<u32>
|
where
|
||||||
|
T: Into<u32>,
|
||||||
{
|
{
|
||||||
self.confirmations() >= target.into()
|
self.confirmations() >= target.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocks_left_until<T>(&self, target: T) -> u32
|
pub fn blocks_left_until<T>(&self, target: T) -> u32
|
||||||
where T: Into<u32>, T: Copy
|
where
|
||||||
|
T: Into<u32>,
|
||||||
|
T: Copy,
|
||||||
{
|
{
|
||||||
if self.meets_target(target) {
|
if self.meets_target(target) {
|
||||||
0
|
0
|
||||||
|
@ -950,7 +953,8 @@ impl ScriptStatus {
|
||||||
|
|
||||||
/// Check if the script has met the given confirmation target.
|
/// Check if the script has met the given confirmation target.
|
||||||
pub fn is_confirmed_with<T>(&self, target: T) -> bool
|
pub fn is_confirmed_with<T>(&self, target: T) -> bool
|
||||||
where T: Into<u32>
|
where
|
||||||
|
T: Into<u32>,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
ScriptStatus::Confirmed(inner) => inner.meets_target(target),
|
ScriptStatus::Confirmed(inner) => inner.meets_target(target),
|
||||||
|
@ -960,12 +964,12 @@ impl ScriptStatus {
|
||||||
|
|
||||||
// Calculate the number of blocks left until the target is met.
|
// Calculate the number of blocks left until the target is met.
|
||||||
pub fn blocks_left_until<T>(&self, target: T) -> u32
|
pub fn blocks_left_until<T>(&self, target: T) -> u32
|
||||||
where T: Into<u32>, T: Copy
|
where
|
||||||
|
T: Into<u32>,
|
||||||
|
T: Copy,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
ScriptStatus::Confirmed(inner) => {
|
ScriptStatus::Confirmed(inner) => inner.blocks_left_until(target),
|
||||||
inner.blocks_left_until(target)
|
|
||||||
}
|
|
||||||
_ => target.into(),
|
_ => target.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use structopt::{clap, StructOpt};
|
use structopt::{clap, StructOpt};
|
||||||
use tokio::sync::broadcast;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -42,10 +41,7 @@ pub enum ParseResult {
|
||||||
PrintAndExitZero { message: String },
|
PrintAndExitZero { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parse_args_and_apply_defaults<I, T>(
|
pub async fn parse_args_and_apply_defaults<I, T>(raw_args: I) -> Result<ParseResult>
|
||||||
raw_args: I,
|
|
||||||
rx: broadcast::Sender<()>,
|
|
||||||
) -> Result<ParseResult>
|
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: Into<OsString> + Clone,
|
T: Into<OsString> + Clone,
|
||||||
|
@ -78,14 +74,12 @@ where
|
||||||
let bitcoin_change_address =
|
let bitcoin_change_address =
|
||||||
bitcoin_address::validate(bitcoin_change_address, is_testnet)?;
|
bitcoin_address::validate(bitcoin_change_address, is_testnet)?;
|
||||||
|
|
||||||
let request = Request::new(
|
let request = Request::new(Method::BuyXmr {
|
||||||
Method::BuyXmr {
|
seller,
|
||||||
seller,
|
bitcoin_change_address,
|
||||||
bitcoin_change_address,
|
monero_receive_address,
|
||||||
monero_receive_address,
|
swap_id: Uuid::new_v4(),
|
||||||
swap_id: Uuid::new_v4(),
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -96,7 +90,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -105,14 +98,14 @@ where
|
||||||
let request = Request::new(Method::History);
|
let request = Request::new(Method::History);
|
||||||
|
|
||||||
let context =
|
let context =
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None, rx).await?;
|
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
}
|
}
|
||||||
CliCommand::Config => {
|
CliCommand::Config => {
|
||||||
let request = Request::new(Method::Config);
|
let request = Request::new(Method::Config);
|
||||||
|
|
||||||
let context =
|
let context =
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None, rx).await?;
|
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
}
|
}
|
||||||
CliCommand::Balance { bitcoin } => {
|
CliCommand::Balance { bitcoin } => {
|
||||||
|
@ -127,7 +120,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -138,11 +130,7 @@ where
|
||||||
monero,
|
monero,
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::StartDaemon { server_address });
|
||||||
Method::StartDaemon {
|
|
||||||
server_address
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -153,7 +141,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
server_address,
|
server_address,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -165,12 +152,7 @@ where
|
||||||
} => {
|
} => {
|
||||||
let address = bitcoin_address::validate(address, is_testnet)?;
|
let address = bitcoin_address::validate(address, is_testnet)?;
|
||||||
|
|
||||||
let request = Request::new(
|
let request = Request::new(Method::WithdrawBtc { amount, address });
|
||||||
Method::WithdrawBtc {
|
|
||||||
amount,
|
|
||||||
address,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -181,7 +163,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -192,11 +173,7 @@ where
|
||||||
monero,
|
monero,
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::Resume { swap_id });
|
||||||
Method::Resume {
|
|
||||||
swap_id
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -207,7 +184,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -217,11 +193,7 @@ where
|
||||||
bitcoin,
|
bitcoin,
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::CancelAndRefund { swap_id });
|
||||||
Method::CancelAndRefund {
|
|
||||||
swap_id
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -232,7 +204,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -241,31 +212,15 @@ where
|
||||||
rendezvous_point,
|
rendezvous_point,
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::ListSellers { rendezvous_point });
|
||||||
Method::ListSellers {
|
|
||||||
rendezvous_point
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context =
|
||||||
None,
|
Context::build(None, None, Some(tor), data, is_testnet, debug, json, None).await?;
|
||||||
None,
|
|
||||||
Some(tor),
|
|
||||||
data,
|
|
||||||
is_testnet,
|
|
||||||
debug,
|
|
||||||
json,
|
|
||||||
None,
|
|
||||||
rx,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
(context, request)
|
(context, request)
|
||||||
}
|
}
|
||||||
CliCommand::ExportBitcoinWallet { bitcoin } => {
|
CliCommand::ExportBitcoinWallet { bitcoin } => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::ExportBitcoinWallet);
|
||||||
Method::ExportBitcoinWallet,
|
|
||||||
);
|
|
||||||
|
|
||||||
let context = Context::build(
|
let context = Context::build(
|
||||||
Some(bitcoin),
|
Some(bitcoin),
|
||||||
|
@ -276,7 +231,6 @@ where
|
||||||
debug,
|
debug,
|
||||||
json,
|
json,
|
||||||
None,
|
None,
|
||||||
rx,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
(context, request)
|
(context, request)
|
||||||
|
@ -284,14 +238,10 @@ where
|
||||||
CliCommand::MoneroRecovery {
|
CliCommand::MoneroRecovery {
|
||||||
swap_id: SwapId { swap_id },
|
swap_id: SwapId { swap_id },
|
||||||
} => {
|
} => {
|
||||||
let request = Request::new(
|
let request = Request::new(Method::MoneroRecovery { swap_id });
|
||||||
Method::MoneroRecovery {
|
|
||||||
swap_id
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let context =
|
let context =
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None, rx).await?;
|
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||||
|
|
||||||
(context, request)
|
(context, request)
|
||||||
}
|
}
|
||||||
|
@ -570,15 +520,12 @@ mod tests {
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -605,15 +552,12 @@ mod tests {
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -639,10 +583,7 @@ mod tests {
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let err = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
let err = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
||||||
|
@ -668,10 +609,7 @@ mod tests {
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let err = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
let err = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
||||||
|
@ -687,15 +625,12 @@ mod tests {
|
||||||
async fn given_resume_on_mainnet_then_defaults_to_mainnet() {
|
async fn given_resume_on_mainnet_then_defaults_to_mainnet() {
|
||||||
let raw_ars = vec![BINARY_NAME, "resume", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "resume", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -713,14 +648,12 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "--testnet", "resume", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "--testnet", "resume", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -738,15 +671,13 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "cancel", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "cancel", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::cancel(tx.clone()),
|
Request::cancel(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -764,14 +695,12 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "--testnet", "cancel", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "--testnet", "cancel", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::cancel(tx.clone()),
|
Request::cancel(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -789,14 +718,12 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "refund", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "refund", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::refund(tx.clone()),
|
Request::refund(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -814,14 +741,12 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "--testnet", "refund", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "--testnet", "refund", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::refund(tx.clone()),
|
Request::refund(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -850,15 +775,13 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -889,14 +812,12 @@ mod tests {
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -922,14 +843,12 @@ mod tests {
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, false);
|
let (is_testnet, debug, json) = (false, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -956,14 +875,12 @@ mod tests {
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, false);
|
let (is_testnet, debug, json) = (true, false, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -991,14 +908,12 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, true, false);
|
let (is_testnet, debug, json) = (false, true, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1027,14 +942,12 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, true, false);
|
let (is_testnet, debug, json) = (true, true, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1052,14 +965,12 @@ mod tests {
|
||||||
let raw_ars = vec![BINARY_NAME, "--debug", "resume", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "--debug", "resume", "--swap-id", SWAP_ID];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, true, false);
|
let (is_testnet, debug, json) = (false, true, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1084,14 +995,12 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, true, false);
|
let (is_testnet, debug, json) = (true, true, false);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1119,15 +1028,13 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, true);
|
let (is_testnet, debug, json) = (false, false, true);
|
||||||
let data_dir = data_dir_path_cli(is_testnet);
|
let data_dir = data_dir_path_cli(is_testnet);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1156,14 +1063,12 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, true);
|
let (is_testnet, debug, json) = (true, false, true);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::buy_xmr(is_testnet, tx.clone()),
|
Request::buy_xmr(is_testnet),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1180,14 +1085,12 @@ mod tests {
|
||||||
async fn given_resume_on_mainnet_with_json_then_json_set() {
|
async fn given_resume_on_mainnet_with_json_then_json_set() {
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let raw_ars = vec![BINARY_NAME, "--json", "resume", "--swap-id", SWAP_ID];
|
let raw_ars = vec![BINARY_NAME, "--json", "resume", "--swap-id", SWAP_ID];
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (false, false, true);
|
let (is_testnet, debug, json) = (false, false, true);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1212,14 +1115,12 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let args = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (is_testnet, debug, json) = (true, false, true);
|
let (is_testnet, debug, json) = (true, false, true);
|
||||||
|
|
||||||
let (expected_config, expected_request) = (
|
let (expected_config, expected_request) = (
|
||||||
Config::default(is_testnet, None, debug, json),
|
Config::default(is_testnet, None, debug, json),
|
||||||
Request::resume(tx.clone()),
|
Request::resume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (actual_config, actual_request) = match args {
|
let (actual_config, actual_request) = match args {
|
||||||
|
@ -1245,9 +1146,7 @@ mod tests {
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let (tx, _) = broadcast::channel(1);
|
let (tx, _) = broadcast::channel(1);
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
let raw_ars = vec![
|
let raw_ars = vec![
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
|
@ -1259,9 +1158,7 @@ mod tests {
|
||||||
"--seller",
|
"--seller",
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
let raw_ars = vec![
|
let raw_ars = vec![
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
|
@ -1273,9 +1170,7 @@ mod tests {
|
||||||
"--seller",
|
"--seller",
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert!(matches!(result, ParseResult::Context(_, _)));
|
assert!(matches!(result, ParseResult::Context(_, _)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1294,9 +1189,7 @@ mod tests {
|
||||||
"--seller",
|
"--seller",
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
let raw_ars = vec![
|
let raw_ars = vec![
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
|
@ -1309,9 +1202,7 @@ mod tests {
|
||||||
"--seller",
|
"--seller",
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
|
||||||
.await
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
let raw_ars = vec![
|
let raw_ars = vec![
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
|
@ -1324,9 +1215,7 @@ mod tests {
|
||||||
"--seller",
|
"--seller",
|
||||||
MULTI_ADDRESS,
|
MULTI_ADDRESS,
|
||||||
];
|
];
|
||||||
let result = parse_args_and_apply_defaults(raw_ars, tx.clone())
|
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert!(matches!(result, ParseResult::Context(_, _)));
|
assert!(matches!(result, ParseResult::Context(_, _)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@ pub fn init(debug: bool, json: bool, dir: impl AsRef<Path>) -> Result<()> {
|
||||||
let level_filter = EnvFilter::try_new("swap=debug")?;
|
let level_filter = EnvFilter::try_new("swap=debug")?;
|
||||||
let registry = Registry::default().with(level_filter);
|
let registry = Registry::default().with(level_filter);
|
||||||
|
|
||||||
let appender =
|
let appender = tracing_appender::rolling::never(dir.as_ref(), "swap-all.log");
|
||||||
tracing_appender::rolling::never(dir.as_ref(), "swap-all.log");
|
|
||||||
let (appender, guard) = tracing_appender::non_blocking(appender);
|
let (appender, guard) = tracing_appender::non_blocking(appender);
|
||||||
|
|
||||||
std::mem::forget(guard);
|
std::mem::forget(guard);
|
||||||
|
@ -47,19 +46,11 @@ pub struct StdErrPrinter<L> {
|
||||||
level: Level,
|
level: Level,
|
||||||
}
|
}
|
||||||
|
|
||||||
type StdErrLayer<S, T> = fmt::Layer<
|
type StdErrLayer<S, T> =
|
||||||
S,
|
fmt::Layer<S, DefaultFields, Format<fmt::format::Full, T>, fn() -> std::io::Stderr>;
|
||||||
DefaultFields,
|
|
||||||
Format<fmt::format::Full, T>,
|
|
||||||
fn() -> std::io::Stderr,
|
|
||||||
>;
|
|
||||||
|
|
||||||
type StdErrJsonLayer<S, T> = fmt::Layer<
|
type StdErrJsonLayer<S, T> =
|
||||||
S,
|
fmt::Layer<S, JsonFields, Format<fmt::format::Json, T>, fn() -> std::io::Stderr>;
|
||||||
JsonFields,
|
|
||||||
Format<fmt::format::Json, T>,
|
|
||||||
fn() -> std::io::Stderr,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn debug_terminal_printer<S>() -> StdErrPrinter<StdErrLayer<S, UtcTime<Rfc3339>>> {
|
fn debug_terminal_printer<S>() -> StdErrPrinter<StdErrLayer<S, UtcTime<Rfc3339>>> {
|
||||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||||
|
|
|
@ -112,7 +112,7 @@ where
|
||||||
}
|
}
|
||||||
AliceState::BtcLocked { state3 } => {
|
AliceState::BtcLocked { state3 } => {
|
||||||
match state3.expired_timelocks(bitcoin_wallet).await? {
|
match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||||
ExpiredTimelocks::None {..} => {
|
ExpiredTimelocks::None { .. } => {
|
||||||
// Record the current monero wallet block height so we don't have to scan from
|
// Record the current monero wallet block height so we don't have to scan from
|
||||||
// block 0 for scenarios where we create a refund wallet.
|
// block 0 for scenarios where we create a refund wallet.
|
||||||
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
|
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
|
||||||
|
@ -135,7 +135,7 @@ where
|
||||||
transfer_proof,
|
transfer_proof,
|
||||||
state3,
|
state3,
|
||||||
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||||
ExpiredTimelocks::None {..} => {
|
ExpiredTimelocks::None { .. } => {
|
||||||
monero_wallet
|
monero_wallet
|
||||||
.watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof.clone(), 1))
|
.watch_for_transfer(state3.lock_xmr_watch_request(transfer_proof.clone(), 1))
|
||||||
.await
|
.await
|
||||||
|
@ -221,7 +221,7 @@ where
|
||||||
encrypted_signature,
|
encrypted_signature,
|
||||||
state3,
|
state3,
|
||||||
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||||
ExpiredTimelocks::None {..} => {
|
ExpiredTimelocks::None { .. } => {
|
||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
||||||
match state3.signed_redeem_transaction(*encrypted_signature) {
|
match state3.signed_redeem_transaction(*encrypted_signature) {
|
||||||
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {
|
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {
|
||||||
|
|
|
@ -117,7 +117,7 @@ async fn next_state(
|
||||||
} => {
|
} => {
|
||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
||||||
|
|
||||||
if let ExpiredTimelocks::None {..} = state3.expired_timelock(bitcoin_wallet).await? {
|
if let ExpiredTimelocks::None { .. } = state3.expired_timelock(bitcoin_wallet).await? {
|
||||||
let transfer_proof_watcher = event_loop_handle.recv_transfer_proof();
|
let transfer_proof_watcher = event_loop_handle.recv_transfer_proof();
|
||||||
let cancel_timelock_expires =
|
let cancel_timelock_expires =
|
||||||
tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock);
|
tx_lock_status.wait_until_confirmed_with(state3.cancel_timelock);
|
||||||
|
@ -156,7 +156,7 @@ async fn next_state(
|
||||||
} => {
|
} => {
|
||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
||||||
|
|
||||||
if let ExpiredTimelocks::None {..} = state.expired_timelock(bitcoin_wallet).await? {
|
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
|
||||||
let watch_request = state.lock_xmr_watch_request(lock_transfer_proof);
|
let watch_request = state.lock_xmr_watch_request(lock_transfer_proof);
|
||||||
|
|
||||||
select! {
|
select! {
|
||||||
|
@ -185,7 +185,7 @@ async fn next_state(
|
||||||
BobState::XmrLocked(state) => {
|
BobState::XmrLocked(state) => {
|
||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
||||||
|
|
||||||
if let ExpiredTimelocks::None {..} = state.expired_timelock(bitcoin_wallet).await? {
|
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
|
||||||
// Alice has locked Xmr
|
// Alice has locked Xmr
|
||||||
// Bob sends Alice his key
|
// Bob sends Alice his key
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ async fn next_state(
|
||||||
BobState::EncSigSent(state) => {
|
BobState::EncSigSent(state) => {
|
||||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
let tx_lock_status = bitcoin_wallet.subscribe_to(state.tx_lock.clone()).await;
|
||||||
|
|
||||||
if let ExpiredTimelocks::None {..} = state.expired_timelock(bitcoin_wallet).await? {
|
if let ExpiredTimelocks::None { .. } = state.expired_timelock(bitcoin_wallet).await? {
|
||||||
select! {
|
select! {
|
||||||
state5 = state.watch_for_redeem_btc(bitcoin_wallet) => {
|
state5 = state.watch_for_redeem_btc(bitcoin_wallet) => {
|
||||||
BobState::BtcRedeemed(state5?)
|
BobState::BtcRedeemed(state5?)
|
||||||
|
@ -269,7 +269,7 @@ async fn next_state(
|
||||||
BobState::BtcCancelled(state) => {
|
BobState::BtcCancelled(state) => {
|
||||||
// Bob has cancelled the swap
|
// Bob has cancelled the swap
|
||||||
match state.expired_timelock(bitcoin_wallet).await? {
|
match state.expired_timelock(bitcoin_wallet).await? {
|
||||||
ExpiredTimelocks::None {..} => {
|
ExpiredTimelocks::None { .. } => {
|
||||||
bail!(
|
bail!(
|
||||||
"Internal error: canceled state reached before cancel timelock was expired"
|
"Internal error: canceled state reached before cancel timelock was expired"
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,7 +26,6 @@ pub async fn run_server(
|
||||||
|
|
||||||
let addr = server.local_addr()?;
|
let addr = server.local_addr()?;
|
||||||
let server_handle = server.start(modules)?;
|
let server_handle = server.start(modules)?;
|
||||||
tracing::info!(%addr, "Started RPC server");
|
|
||||||
|
|
||||||
Ok((addr, server_handle))
|
Ok((addr, server_handle))
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,12 @@ use uuid::Uuid;
|
||||||
pub fn register_modules(context: Arc<Context>) -> RpcModule<Arc<Context>> {
|
pub fn register_modules(context: Arc<Context>) -> RpcModule<Arc<Context>> {
|
||||||
let mut module = RpcModule::new(context);
|
let mut module = RpcModule::new(context);
|
||||||
|
|
||||||
|
module
|
||||||
|
.register_async_method("suspend_current_swap", |_, context| async move {
|
||||||
|
execute_request(Method::SuspendCurrentSwap, &context).await
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
module
|
module
|
||||||
.register_async_method("get_swap_info", |params, context| async move {
|
.register_async_method("get_swap_info", |params, context| async move {
|
||||||
let params: HashMap<String, Uuid> = params.parse()?;
|
let params: HashMap<String, Uuid> = params.parse()?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue