mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-24 23:19:34 -05:00
Activate mainnet for the CLI
This includes testing CLI commandline args Clap's `default_value_with` actually did not work on `Subcommand`s because the parent's flags were not picked up. This was fixed by changing parameters dependent on testnet/mainnet to options. This problem should have been detected by tests, that's why the command line parameter tests were finally (re-)added. Thanks to @rishflab for some pre-work for this.
This commit is contained in:
parent
1cdc23de32
commit
69cf12620d
@ -15,21 +15,21 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use prettytable::{row, Table};
|
||||
use std::cmp::min;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use structopt::StructOpt;
|
||||
use swap::bitcoin::TxLock;
|
||||
use swap::cli::command::{Arguments, Bitcoin, Command, Monero, SellerAddr, SwapId, Tor};
|
||||
use swap::cli::command::{parse_args_and_apply_defaults, Arguments, Command};
|
||||
use swap::database::Database;
|
||||
use swap::env::{Config, GetConfig};
|
||||
use swap::env::Config;
|
||||
use swap::network::quote::BidQuote;
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::bob;
|
||||
use swap::protocol::bob::{EventLoop, Swap};
|
||||
use swap::seed::Seed;
|
||||
use swap::{bitcoin, cli, env, monero};
|
||||
use swap::{bitcoin, cli, monero};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
@ -39,44 +39,33 @@ extern crate prettytable;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let Arguments { data, debug, cmd } = Arguments::from_args();
|
||||
let Arguments {
|
||||
env_config,
|
||||
data_dir,
|
||||
debug,
|
||||
cmd,
|
||||
} = parse_args_and_apply_defaults(env::args_os())?;
|
||||
|
||||
match cmd {
|
||||
Command::BuyXmr {
|
||||
seller_peer_id,
|
||||
seller_addr: SellerAddr { seller_addr },
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
monero:
|
||||
Monero {
|
||||
receive_monero_address,
|
||||
monero_daemon_address,
|
||||
},
|
||||
tor: Tor { tor_socks5_port },
|
||||
seller_addr,
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
monero_receive_address,
|
||||
monero_daemon_address,
|
||||
tor_socks5_port,
|
||||
} => {
|
||||
let swap_id = Uuid::new_v4();
|
||||
|
||||
let data_dir = data.0;
|
||||
cli::tracing::init(debug, data_dir.join("logs"), swap_id)?;
|
||||
let db = Database::open(data_dir.join("database").as_path())
|
||||
.context("Failed to open database")?;
|
||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
.context("Failed to read in seed file")?;
|
||||
let env_config = env::Testnet::get_config();
|
||||
|
||||
if receive_monero_address.network != env_config.monero_network {
|
||||
bail!(
|
||||
"Given monero address is on network {:?}, expected address on network {:?}",
|
||||
receive_monero_address.network,
|
||||
env_config.monero_network
|
||||
)
|
||||
}
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(
|
||||
electrum_rpc_url,
|
||||
bitcoin_electrum_rpc_url,
|
||||
&seed,
|
||||
data_dir.clone(),
|
||||
env_config,
|
||||
@ -118,7 +107,7 @@ async fn main() -> Result<()> {
|
||||
Arc::new(monero_wallet),
|
||||
env_config,
|
||||
event_loop_handle,
|
||||
receive_monero_address,
|
||||
monero_receive_address,
|
||||
send_bitcoin,
|
||||
);
|
||||
|
||||
@ -133,8 +122,6 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
Command::History => {
|
||||
let data_dir = data.0;
|
||||
|
||||
let db = Database::open(data_dir.join("database").as_path())
|
||||
.context("Failed to open database")?;
|
||||
|
||||
@ -150,34 +137,26 @@ async fn main() -> Result<()> {
|
||||
table.printstd();
|
||||
}
|
||||
Command::Resume {
|
||||
swap_id: SwapId { swap_id },
|
||||
seller_addr: SellerAddr { seller_addr },
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
monero:
|
||||
Monero {
|
||||
receive_monero_address,
|
||||
monero_daemon_address,
|
||||
},
|
||||
tor: Tor { tor_socks5_port },
|
||||
swap_id,
|
||||
seller_addr,
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
monero_receive_address,
|
||||
monero_daemon_address,
|
||||
tor_socks5_port,
|
||||
} => {
|
||||
let data_dir = data.0;
|
||||
cli::tracing::init(debug, data_dir.join("logs"), swap_id)?;
|
||||
let db = Database::open(data_dir.join("database").as_path())
|
||||
.context("Failed to open database")?;
|
||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
.context("Failed to read in seed file")?;
|
||||
let env_config = env::Testnet::get_config();
|
||||
|
||||
if receive_monero_address.network != env_config.monero_network {
|
||||
bail!("The given monero address is on network {:?}, expected address of network {:?}.", receive_monero_address.network, env_config.monero_network)
|
||||
if monero_receive_address.network != env_config.monero_network {
|
||||
bail!("The given monero address is on network {:?}, expected address of network {:?}.", monero_receive_address.network, env_config.monero_network)
|
||||
}
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(
|
||||
electrum_rpc_url,
|
||||
bitcoin_electrum_rpc_url,
|
||||
&seed,
|
||||
data_dir.clone(),
|
||||
env_config,
|
||||
@ -208,7 +187,7 @@ async fn main() -> Result<()> {
|
||||
Arc::new(monero_wallet),
|
||||
env_config,
|
||||
event_loop_handle,
|
||||
receive_monero_address,
|
||||
monero_receive_address,
|
||||
)?;
|
||||
|
||||
tokio::select! {
|
||||
@ -221,24 +200,19 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
Command::Cancel {
|
||||
swap_id: SwapId { swap_id },
|
||||
swap_id,
|
||||
force,
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
} => {
|
||||
let data_dir = data.0;
|
||||
cli::tracing::init(debug, data_dir.join("logs"), swap_id)?;
|
||||
let db = Database::open(data_dir.join("database").as_path())
|
||||
.context("Failed to open database")?;
|
||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
.context("Failed to read in seed file")?;
|
||||
let env_config = env::Testnet::get_config();
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(
|
||||
electrum_rpc_url,
|
||||
bitcoin_electrum_rpc_url,
|
||||
&seed,
|
||||
data_dir,
|
||||
env_config,
|
||||
@ -258,24 +232,19 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
Command::Refund {
|
||||
swap_id: SwapId { swap_id },
|
||||
swap_id,
|
||||
force,
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
} => {
|
||||
let data_dir = data.0;
|
||||
cli::tracing::init(debug, data_dir.join("logs"), swap_id)?;
|
||||
let db = Database::open(data_dir.join("database").as_path())
|
||||
.context("Failed to open database")?;
|
||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
.context("Failed to read in seed file")?;
|
||||
let env_config = env::Testnet::get_config();
|
||||
|
||||
let bitcoin_wallet = init_bitcoin_wallet(
|
||||
electrum_rpc_url,
|
||||
bitcoin_electrum_rpc_url,
|
||||
&seed,
|
||||
data_dir,
|
||||
env_config,
|
||||
|
@ -1,38 +1,236 @@
|
||||
use crate::env::GetConfig;
|
||||
use crate::fs::system_data_dir;
|
||||
use crate::{env, monero};
|
||||
use anyhow::{Context, Result};
|
||||
use libp2p::core::Multiaddr;
|
||||
use libp2p::PeerId;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use structopt::StructOpt;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const DEFAULT_STAGENET_MONERO_DAEMON_ADDRESS: &str = "monero-stagenet.exan.tech:38081";
|
||||
// See: https://moneroworld.com/
|
||||
pub const DEFAULT_MONERO_DAEMON_ADDRESS: &str = "node.moneroworld.com:18089";
|
||||
pub const DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET: &str = "monero-stagenet.exan.tech:38081";
|
||||
|
||||
const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002";
|
||||
const DEFAULT_BITCOIN_CONFIRMATION_TARGET: &str = "3";
|
||||
// See: https://1209k.com/bitcoin-eye/ele.php?chain=btc
|
||||
const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:50002";
|
||||
// See: https://1209k.com/bitcoin-eye/ele.php?chain=tbtc
|
||||
pub const DEFAULT_ELECTRUM_RPC_URL_TESTNET: &str = "ssl://electrum.blockstream.info:60002";
|
||||
|
||||
const DEFAULT_BITCOIN_CONFIRMATION_TARGET: usize = 3;
|
||||
const DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET: usize = 1;
|
||||
|
||||
const DEFAULT_TOR_SOCKS5_PORT: &str = "9050";
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Arguments {
|
||||
pub env_config: env::Config,
|
||||
pub debug: bool,
|
||||
pub data_dir: PathBuf,
|
||||
pub cmd: Command,
|
||||
}
|
||||
|
||||
pub fn parse_args_and_apply_defaults<I, T>(raw_args: I) -> Result<Arguments>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
let matches = RawArguments::clap().get_matches_from_safe(raw_args)?;
|
||||
let args = RawArguments::from_clap(&matches);
|
||||
|
||||
let debug = args.debug;
|
||||
let is_testnet = args.testnet;
|
||||
let data = args.data;
|
||||
|
||||
match args.cmd {
|
||||
RawCommand::BuyXmr {
|
||||
seller_peer_id,
|
||||
seller_addr: SellerAddr { seller_addr },
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
monero:
|
||||
Monero {
|
||||
monero_receive_address,
|
||||
monero_daemon_address,
|
||||
},
|
||||
tor: Tor { tor_socks5_port },
|
||||
} => Ok(Arguments {
|
||||
env_config: env_config_from(is_testnet),
|
||||
debug,
|
||||
data_dir: data::data_dir_from(data, is_testnet)?,
|
||||
cmd: Command::BuyXmr {
|
||||
seller_peer_id,
|
||||
seller_addr,
|
||||
bitcoin_electrum_rpc_url: bitcoin_electrum_rpc_url_from(
|
||||
bitcoin_electrum_rpc_url,
|
||||
is_testnet,
|
||||
)?,
|
||||
bitcoin_target_block: bitcoin_target_block_from(bitcoin_target_block, is_testnet),
|
||||
monero_receive_address: validate_monero_address(
|
||||
monero_receive_address,
|
||||
is_testnet,
|
||||
)?,
|
||||
monero_daemon_address: monero_daemon_address_from(
|
||||
monero_daemon_address,
|
||||
is_testnet,
|
||||
),
|
||||
tor_socks5_port,
|
||||
},
|
||||
}),
|
||||
RawCommand::History => Ok(Arguments {
|
||||
env_config: env_config_from(is_testnet),
|
||||
debug,
|
||||
data_dir: data::data_dir_from(data, is_testnet)?,
|
||||
cmd: Command::History,
|
||||
}),
|
||||
RawCommand::Resume {
|
||||
swap_id: SwapId { swap_id },
|
||||
seller_addr: SellerAddr { seller_addr },
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
monero:
|
||||
Monero {
|
||||
monero_receive_address,
|
||||
monero_daemon_address,
|
||||
},
|
||||
tor: Tor { tor_socks5_port },
|
||||
} => Ok(Arguments {
|
||||
env_config: env_config_from(is_testnet),
|
||||
debug,
|
||||
data_dir: data::data_dir_from(data, is_testnet)?,
|
||||
cmd: Command::Resume {
|
||||
swap_id,
|
||||
seller_addr,
|
||||
bitcoin_electrum_rpc_url: bitcoin_electrum_rpc_url_from(
|
||||
bitcoin_electrum_rpc_url,
|
||||
is_testnet,
|
||||
)?,
|
||||
bitcoin_target_block: bitcoin_target_block_from(bitcoin_target_block, is_testnet),
|
||||
monero_receive_address,
|
||||
monero_daemon_address: monero_daemon_address_from(
|
||||
monero_daemon_address,
|
||||
is_testnet,
|
||||
),
|
||||
tor_socks5_port,
|
||||
},
|
||||
}),
|
||||
RawCommand::Cancel {
|
||||
swap_id: SwapId { swap_id },
|
||||
force,
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
} => Ok(Arguments {
|
||||
env_config: env_config_from(is_testnet),
|
||||
debug,
|
||||
data_dir: data::data_dir_from(data, is_testnet)?,
|
||||
cmd: Command::Cancel {
|
||||
swap_id,
|
||||
force,
|
||||
bitcoin_electrum_rpc_url: bitcoin_electrum_rpc_url_from(
|
||||
bitcoin_electrum_rpc_url,
|
||||
is_testnet,
|
||||
)?,
|
||||
bitcoin_target_block: bitcoin_target_block_from(bitcoin_target_block, is_testnet),
|
||||
},
|
||||
}),
|
||||
RawCommand::Refund {
|
||||
swap_id: SwapId { swap_id },
|
||||
force,
|
||||
bitcoin:
|
||||
Bitcoin {
|
||||
bitcoin_electrum_rpc_url,
|
||||
bitcoin_target_block,
|
||||
},
|
||||
} => Ok(Arguments {
|
||||
env_config: env_config_from(is_testnet),
|
||||
debug,
|
||||
data_dir: data::data_dir_from(data, is_testnet)?,
|
||||
cmd: Command::Refund {
|
||||
swap_id,
|
||||
force,
|
||||
bitcoin_electrum_rpc_url: bitcoin_electrum_rpc_url_from(
|
||||
bitcoin_electrum_rpc_url,
|
||||
is_testnet,
|
||||
)?,
|
||||
bitcoin_target_block: bitcoin_target_block_from(bitcoin_target_block, is_testnet),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Command {
|
||||
BuyXmr {
|
||||
seller_peer_id: PeerId,
|
||||
seller_addr: Multiaddr,
|
||||
bitcoin_electrum_rpc_url: Url,
|
||||
bitcoin_target_block: usize,
|
||||
monero_receive_address: monero::Address,
|
||||
monero_daemon_address: String,
|
||||
tor_socks5_port: u16,
|
||||
},
|
||||
History,
|
||||
Resume {
|
||||
swap_id: Uuid,
|
||||
seller_addr: Multiaddr,
|
||||
bitcoin_electrum_rpc_url: Url,
|
||||
bitcoin_target_block: usize,
|
||||
monero_receive_address: monero::Address,
|
||||
monero_daemon_address: String,
|
||||
tor_socks5_port: u16,
|
||||
},
|
||||
Cancel {
|
||||
swap_id: Uuid,
|
||||
force: bool,
|
||||
bitcoin_electrum_rpc_url: Url,
|
||||
bitcoin_target_block: usize,
|
||||
},
|
||||
Refund {
|
||||
swap_id: Uuid,
|
||||
force: bool,
|
||||
bitcoin_electrum_rpc_url: Url,
|
||||
bitcoin_target_block: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(structopt::StructOpt, Debug)]
|
||||
#[structopt(name = "swap", about = "CLI for swapping BTC for XMR", author)]
|
||||
pub struct Arguments {
|
||||
pub struct RawArguments {
|
||||
// global is necessary to ensure that clap can match against testnet in subcommands
|
||||
#[structopt(
|
||||
long,
|
||||
help = "Swap on testnet and assume testnet defaults for data-dir and the blockchain related parameters",
|
||||
global = true
|
||||
)]
|
||||
pub testnet: bool,
|
||||
|
||||
#[structopt(
|
||||
long = "--data-dir",
|
||||
help = "Provide the data directory path to be used to store application data",
|
||||
default_value
|
||||
help = "Provide the data directory path to be used to store application data using testnet and mainnet as subfolder"
|
||||
)]
|
||||
pub data: Data,
|
||||
pub data: Option<PathBuf>,
|
||||
|
||||
#[structopt(long, help = "Activate debug logging.")]
|
||||
pub debug: bool,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
pub cmd: Command,
|
||||
pub cmd: RawCommand,
|
||||
}
|
||||
|
||||
#[derive(structopt::StructOpt, Debug)]
|
||||
pub enum Command {
|
||||
pub enum RawCommand {
|
||||
/// Start a XMR for BTC swap
|
||||
BuyXmr {
|
||||
#[structopt(long = "seller-peer-id", help = "The seller's peer id")]
|
||||
@ -99,31 +297,34 @@ pub struct Monero {
|
||||
help = "Provide the monero address where you would like to receive monero",
|
||||
parse(try_from_str = parse_monero_address)
|
||||
)]
|
||||
pub receive_monero_address: monero::Address,
|
||||
pub monero_receive_address: monero::Address,
|
||||
|
||||
#[structopt(
|
||||
long = "monero-daemon-address",
|
||||
help = "Specify to connect to a monero daemon of your choice: <host>:<port>",
|
||||
default_value = DEFAULT_STAGENET_MONERO_DAEMON_ADDRESS
|
||||
help = "Specify to connect to a monero daemon of your choice: <host>:<port>"
|
||||
)]
|
||||
pub monero_daemon_address: String,
|
||||
pub monero_daemon_address: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(structopt::StructOpt, Debug)]
|
||||
pub struct Bitcoin {
|
||||
#[structopt(long = "electrum-rpc",
|
||||
help = "Provide the Bitcoin Electrum RPC URL",
|
||||
default_value = DEFAULT_ELECTRUM_RPC_URL
|
||||
)]
|
||||
pub electrum_rpc_url: Url,
|
||||
#[structopt(long = "electrum-rpc", help = "Provide the Bitcoin Electrum RPC URL")]
|
||||
pub bitcoin_electrum_rpc_url: Option<Url>,
|
||||
|
||||
#[structopt(long = "bitcoin-target-block", help = "Within how many blocks should the Bitcoin transactions be confirmed.", default_value = DEFAULT_BITCOIN_CONFIRMATION_TARGET)]
|
||||
pub bitcoin_target_block: usize,
|
||||
#[structopt(
|
||||
long = "bitcoin-target-block",
|
||||
help = "Use for fee estimation, decides within how many blocks the Bitcoin transactions should be confirmed."
|
||||
)]
|
||||
pub bitcoin_target_block: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(structopt::StructOpt, Debug)]
|
||||
pub struct Tor {
|
||||
#[structopt(long = "tor-socks5-port", help = "Your local Tor socks5 proxy port", default_value = DEFAULT_TOR_SOCKS5_PORT)]
|
||||
#[structopt(
|
||||
long = "tor-socks5-port",
|
||||
help = "Your local Tor socks5 proxy port",
|
||||
default_value = DEFAULT_TOR_SOCKS5_PORT
|
||||
)]
|
||||
pub tor_socks5_port: u16,
|
||||
}
|
||||
|
||||
@ -142,39 +343,92 @@ pub struct SellerAddr {
|
||||
pub seller_addr: Multiaddr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Data(pub PathBuf);
|
||||
mod data {
|
||||
use super::*;
|
||||
|
||||
/// Default location for storing data for the CLI
|
||||
// Takes the default system data-dir and adds a `/cli`
|
||||
impl Default for Data {
|
||||
fn default() -> Self {
|
||||
Data(
|
||||
system_data_dir()
|
||||
.map(|proj_dir| Path::join(&proj_dir, "cli"))
|
||||
.expect("computed valid path for data dir"),
|
||||
)
|
||||
pub fn data_dir_from(arg_dir: Option<PathBuf>, testnet: bool) -> Result<PathBuf> {
|
||||
let dir = if let Some(dir) = arg_dir {
|
||||
dir
|
||||
} else if testnet {
|
||||
testnet_default()?
|
||||
} else {
|
||||
mainnet_default()?
|
||||
};
|
||||
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
fn testnet_default() -> Result<PathBuf> {
|
||||
Ok(os_default()?.join("testnet"))
|
||||
}
|
||||
|
||||
fn mainnet_default() -> Result<PathBuf> {
|
||||
Ok(os_default()?.join("mainnet"))
|
||||
}
|
||||
|
||||
fn os_default() -> Result<PathBuf> {
|
||||
Ok(system_data_dir()?.join("cli"))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Data {
|
||||
type Err = core::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Data(PathBuf::from_str(s)?))
|
||||
fn bitcoin_electrum_rpc_url_from(url: Option<Url>, testnet: bool) -> Result<Url> {
|
||||
if let Some(url) = url {
|
||||
Ok(url)
|
||||
} else if testnet {
|
||||
Ok(Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)?)
|
||||
} else {
|
||||
Ok(Url::from_str(DEFAULT_ELECTRUM_RPC_URL)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Data {
|
||||
fn to_string(&self) -> String {
|
||||
self.0
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("default datadir to be convertible to string")
|
||||
fn bitcoin_target_block_from(target_block: Option<usize>, testnet: bool) -> usize {
|
||||
if let Some(target_block) = target_block {
|
||||
target_block
|
||||
} else if testnet {
|
||||
DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET
|
||||
} else {
|
||||
DEFAULT_BITCOIN_CONFIRMATION_TARGET
|
||||
}
|
||||
}
|
||||
|
||||
fn monero_daemon_address_from(address: Option<String>, testnet: bool) -> String {
|
||||
if let Some(address) = address {
|
||||
address
|
||||
} else if testnet {
|
||||
DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string()
|
||||
} else {
|
||||
DEFAULT_MONERO_DAEMON_ADDRESS.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn env_config_from(testnet: bool) -> env::Config {
|
||||
if testnet {
|
||||
env::Testnet::get_config()
|
||||
} else {
|
||||
env::Mainnet::get_config()
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_monero_address(
|
||||
address: monero::Address,
|
||||
testnet: bool,
|
||||
) -> Result<monero::Address, MoneroAddressNetworkMismatch> {
|
||||
let expected_network = if testnet {
|
||||
monero::Network::Stagenet
|
||||
} else {
|
||||
monero::Network::Mainnet
|
||||
};
|
||||
|
||||
if address.network != expected_network {
|
||||
return Err(MoneroAddressNetworkMismatch {
|
||||
expected: expected_network,
|
||||
actual: address.network,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
fn parse_monero_address(s: &str) -> Result<monero::Address> {
|
||||
monero::Address::from_str(s).with_context(|| {
|
||||
format!(
|
||||
@ -183,3 +437,479 @@ fn parse_monero_address(s: &str) -> Result<monero::Address> {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq)]
|
||||
#[error("Invalid monero address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
|
||||
pub struct MoneroAddressNetworkMismatch {
|
||||
expected: monero::Network,
|
||||
actual: monero::Network,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tor::DEFAULT_SOCKS5_PORT;
|
||||
|
||||
const BINARY_NAME: &str = "swap";
|
||||
|
||||
const TESTNET: &str = "testnet";
|
||||
const MAINNET: &str = "mainnet";
|
||||
|
||||
const MONERO_STAGENET_ADDRESS: &str = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a";
|
||||
const MONERO_MAINNET_ADDRESS: &str = "44Ato7HveWidJYUAVw5QffEcEtSH1DwzSP3FPPkHxNAS4LX9CqgucphTisH978FLHE34YNEx7FcbBfQLQUU8m3NUC4VqsRa";
|
||||
const MUTLI_ADDRESS: &str = "/ip4/127.0.0.1/tcp/9939";
|
||||
const PEER_ID: &str = "12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi";
|
||||
const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b";
|
||||
|
||||
#[test]
|
||||
fn given_buy_xmr_on_mainnet_then_defaults_to_mainnet() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let expected_args = Arguments::buy_xmr_mainnet_defaults();
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(expected_args, args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_buy_xmr_on_testnet_then_defaults_to_testnet() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::buy_xmr_testnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_buy_xmr_on_mainnet_with_testnet_address_then_fails() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let err = parse_args_and_apply_defaults(raw_ars).unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
||||
&MoneroAddressNetworkMismatch {
|
||||
expected: monero::Network::Mainnet,
|
||||
actual: monero::Network::Stagenet
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_buy_xmr_on_testnet_with_mainnet_address_then_fails() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let err = parse_args_and_apply_defaults(raw_ars).unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err.downcast_ref::<MoneroAddressNetworkMismatch>().unwrap(),
|
||||
&MoneroAddressNetworkMismatch {
|
||||
expected: monero::Network::Stagenet,
|
||||
actual: monero::Network::Mainnet
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_resume_on_mainnet_then_defaults_to_mainnet() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::resume_mainnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_resume_on_testnet_then_defaults_to_testnet() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::resume_testnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_cancel_on_mainnet_then_defaults_to_mainnet() {
|
||||
let raw_ars = vec![BINARY_NAME, "cancel", "--swap-id", SWAP_ID];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::cancel_mainnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_cancel_on_testnet_then_defaults_to_testnet() {
|
||||
let raw_ars = vec![BINARY_NAME, "--testnet", "cancel", "--swap-id", SWAP_ID];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::cancel_testnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_refund_on_mainnet_then_defaults_to_mainnet() {
|
||||
let raw_ars = vec![BINARY_NAME, "refund", "--swap-id", SWAP_ID];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::refund_mainnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_refund_on_testnet_then_defaults_to_testnet() {
|
||||
let raw_ars = vec![BINARY_NAME, "--testnet", "refund", "--swap-id", SWAP_ID];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::refund_testnet_defaults());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_with_data_dir_then_data_dir_set() {
|
||||
let data_dir = "/some/path/to/dir";
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--data-dir",
|
||||
data_dir,
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(
|
||||
args,
|
||||
Arguments::buy_xmr_mainnet_defaults()
|
||||
.with_data_dir(PathBuf::from_str(data_dir).unwrap())
|
||||
);
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"--data-dir",
|
||||
data_dir,
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(
|
||||
args,
|
||||
Arguments::buy_xmr_testnet_defaults()
|
||||
.with_data_dir(PathBuf::from_str(data_dir).unwrap())
|
||||
);
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--data-dir",
|
||||
data_dir,
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(
|
||||
args,
|
||||
Arguments::resume_mainnet_defaults()
|
||||
.with_data_dir(PathBuf::from_str(data_dir).unwrap())
|
||||
);
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"--data-dir",
|
||||
data_dir,
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(
|
||||
args,
|
||||
Arguments::resume_testnet_defaults()
|
||||
.with_data_dir(PathBuf::from_str(data_dir).unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn given_with_debug_then_debug_set() {
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--debug",
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::buy_xmr_mainnet_defaults().with_debug());
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"--debug",
|
||||
"buy-xmr",
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
"--seller-peer-id",
|
||||
PEER_ID,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::buy_xmr_testnet_defaults().with_debug());
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--debug",
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_MAINNET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::resume_mainnet_defaults().with_debug());
|
||||
|
||||
let raw_ars = vec![
|
||||
BINARY_NAME,
|
||||
"--testnet",
|
||||
"--debug",
|
||||
"resume",
|
||||
"--swap-id",
|
||||
SWAP_ID,
|
||||
"--receive-address",
|
||||
MONERO_STAGENET_ADDRESS,
|
||||
"--seller-addr",
|
||||
MUTLI_ADDRESS,
|
||||
];
|
||||
|
||||
let args = parse_args_and_apply_defaults(raw_ars).unwrap();
|
||||
assert_eq!(args, Arguments::resume_testnet_defaults().with_debug());
|
||||
}
|
||||
|
||||
impl Arguments {
|
||||
pub fn buy_xmr_testnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Testnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(TESTNET),
|
||||
cmd: Command::BuyXmr {
|
||||
seller_peer_id: PeerId::from_str(PEER_ID).unwrap(),
|
||||
seller_addr: Multiaddr::from_str(MUTLI_ADDRESS).unwrap(),
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
|
||||
.unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET,
|
||||
monero_receive_address: monero::Address::from_str(MONERO_STAGENET_ADDRESS)
|
||||
.unwrap(),
|
||||
monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string(),
|
||||
tor_socks5_port: DEFAULT_SOCKS5_PORT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buy_xmr_mainnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Mainnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(MAINNET),
|
||||
cmd: Command::BuyXmr {
|
||||
seller_peer_id: PeerId::from_str(PEER_ID).unwrap(),
|
||||
seller_addr: Multiaddr::from_str(MUTLI_ADDRESS).unwrap(),
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
|
||||
monero_receive_address: monero::Address::from_str(MONERO_MAINNET_ADDRESS)
|
||||
.unwrap(),
|
||||
monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS.to_string(),
|
||||
tor_socks5_port: DEFAULT_SOCKS5_PORT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resume_testnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Testnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(TESTNET),
|
||||
cmd: Command::Resume {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
seller_addr: Multiaddr::from_str(MUTLI_ADDRESS).unwrap(),
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
|
||||
.unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET,
|
||||
monero_receive_address: monero::Address::from_str(MONERO_STAGENET_ADDRESS)
|
||||
.unwrap(),
|
||||
monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET.to_string(),
|
||||
tor_socks5_port: DEFAULT_SOCKS5_PORT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resume_mainnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Mainnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(MAINNET),
|
||||
cmd: Command::Resume {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
seller_addr: Multiaddr::from_str(MUTLI_ADDRESS).unwrap(),
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
|
||||
monero_receive_address: monero::Address::from_str(MONERO_MAINNET_ADDRESS)
|
||||
.unwrap(),
|
||||
monero_daemon_address: DEFAULT_MONERO_DAEMON_ADDRESS.to_string(),
|
||||
tor_socks5_port: DEFAULT_SOCKS5_PORT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_testnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Testnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(TESTNET),
|
||||
cmd: Command::Cancel {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
force: false,
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
|
||||
.unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_mainnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Mainnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(MAINNET),
|
||||
cmd: Command::Cancel {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
force: false,
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refund_testnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Testnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(TESTNET),
|
||||
cmd: Command::Refund {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
force: false,
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
|
||||
.unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET_TESTNET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refund_mainnet_defaults() -> Self {
|
||||
Self {
|
||||
env_config: env::Mainnet::get_config(),
|
||||
debug: false,
|
||||
data_dir: data_dir_path_cli().join(MAINNET),
|
||||
cmd: Command::Refund {
|
||||
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
|
||||
force: false,
|
||||
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_data_dir(mut self, data_dir: PathBuf) -> Self {
|
||||
self.data_dir = data_dir;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_debug(mut self) -> Self {
|
||||
self.debug = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn data_dir_path_cli() -> PathBuf {
|
||||
system_data_dir().unwrap().join("cli")
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::cmp::max;
|
||||
use std::time::Duration;
|
||||
use time::NumericalStdDurationShort;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
pub bitcoin_lock_confirmed_timeout: Duration,
|
||||
pub bitcoin_finality_confirmations: u32,
|
||||
|
Loading…
Reference in New Issue
Block a user