diff --git a/swap/src/bin/cli.rs b/swap/src/bin/cli.rs index 4d8950cb..c59bcfe8 100644 --- a/swap/src/bin/cli.rs +++ b/swap/src/bin/cli.rs @@ -15,20 +15,21 @@ use anyhow::{Context, Result}; use log::LevelFilter; use prettytable::{row, Table}; -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use structopt::StructOpt; use swap::{ bitcoin, cli::{ command::{Arguments, Cancel, Command, Refund, Resume}, config::{ - initial_setup, query_user_for_initial_testnet_config, read_config, ConfigNotInitialized, + initial_setup, query_user_for_initial_testnet_config, read_config, Config, + ConfigNotInitialized, }, }, database::Database, execution_params, execution_params::GetExecutionParams, - fs::{default_config_path, default_data_dir}, + fs::default_config_path, monero, monero::{CreateWallet, OpenWallet}, protocol::{ @@ -53,19 +54,30 @@ async fn main() -> Result<()> { let opt = Arguments::from_args(); - let data_dir = if let Some(data_dir) = opt.data_dir { - data_dir + let config_path = if let Some(config_path) = opt.config { + config_path } else { - default_data_dir().context("unable to determine default data path")? + default_config_path()? + }; + + let config = match read_config(config_path.clone())? { + Ok(config) => config, + Err(ConfigNotInitialized {}) => { + initial_setup(config_path.clone(), query_user_for_initial_testnet_config)?; + read_config(config_path)?.expect("after initial setup config can be read") + } }; info!( "Database and Seed will be stored in directory: {}", - data_dir.display() + config.data.dir.display() ); - let db_path = data_dir.join("database"); - let seed = Seed::from_file_or_generate(&data_dir).expect("Could not retrieve/initialize seed"); + let db = Database::open(config.data.dir.join("database").as_path()) + .context("Could not open database")?; + + let seed = + Seed::from_file_or_generate(&config.data.dir).expect("Could not retrieve/initialize seed"); // hardcode to testnet/stagenet let bitcoin_network = bitcoin::Network::Testnet; @@ -78,7 +90,6 @@ async fn main() -> Result<()> { alice_addr, send_bitcoin, receive_monero, - config, } => { let swap_amounts = SwapAmounts { btc: send_bitcoin, @@ -86,7 +97,7 @@ async fn main() -> Result<()> { }; let (bitcoin_wallet, monero_wallet) = - init_wallets(config.path, bitcoin_network, monero_network).await?; + init_wallets(config, bitcoin_network, monero_network).await?; let swap_id = Uuid::new_v4(); @@ -97,7 +108,7 @@ async fn main() -> Result<()> { let bob_factory = Builder::new( seed, - db_path, + db, swap_id, Arc::new(bitcoin_wallet), Arc::new(monero_wallet), @@ -115,8 +126,6 @@ async fn main() -> Result<()> { table.add_row(row!["SWAP ID", "STATE"]); - let db = Database::open(db_path.as_path()).context("Could not open database")?; - for (swap_id, state) in db.all()? { table.add_row(row![swap_id, state]); } @@ -128,14 +137,13 @@ async fn main() -> Result<()> { swap_id, alice_peer_id, alice_addr, - config, }) => { let (bitcoin_wallet, monero_wallet) = - init_wallets(config.path, bitcoin_network, monero_network).await?; + init_wallets(config, bitcoin_network, monero_network).await?; let bob_factory = Builder::new( seed, - db_path, + db, swap_id, Arc::new(bitcoin_wallet), Arc::new(monero_wallet), @@ -152,16 +160,15 @@ async fn main() -> Result<()> { swap_id, alice_peer_id, alice_addr, - config, force, }) => { // TODO: Optimization: Only init the Bitcoin wallet, Monero wallet unnecessary let (bitcoin_wallet, monero_wallet) = - init_wallets(config.path, bitcoin_network, monero_network).await?; + init_wallets(config, bitcoin_network, monero_network).await?; let bob_factory = Builder::new( seed, - db_path, + db, swap_id, Arc::new(bitcoin_wallet), Arc::new(monero_wallet), @@ -198,16 +205,15 @@ async fn main() -> Result<()> { swap_id, alice_peer_id, alice_addr, - config, force, }) => { let (bitcoin_wallet, monero_wallet) = - init_wallets(config.path, bitcoin_network, monero_network).await?; + init_wallets(config, bitcoin_network, monero_network).await?; // TODO: Optimize to only use the Bitcoin wallet, Monero wallet is unnecessary let bob_factory = Builder::new( seed, - db_path, + db, swap_id, Arc::new(bitcoin_wallet), Arc::new(monero_wallet), @@ -234,24 +240,10 @@ async fn main() -> Result<()> { } async fn init_wallets( - config_path: Option, + config: Config, bitcoin_network: bitcoin::Network, monero_network: monero::Network, ) -> Result<(bitcoin::Wallet, monero::Wallet)> { - let config_path = if let Some(config_path) = config_path { - config_path - } else { - default_config_path()? - }; - - let config = match read_config(config_path.clone())? { - Ok(config) => config, - Err(ConfigNotInitialized {}) => { - initial_setup(config_path.clone(), query_user_for_initial_testnet_config)?; - read_config(config_path)?.expect("after initial setup config can be read") - } - }; - let bitcoin_wallet = bitcoin::Wallet::new( config.bitcoin.wallet_name.as_str(), config.bitcoin.bitcoind_url, diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 67cdcb80..1cf2f4a6 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -6,11 +6,11 @@ use uuid::Uuid; #[derive(structopt::StructOpt, Debug)] pub struct Arguments { #[structopt( - long = "data-dir", - help = "Provide a custom path to the data directory.", + long = "config", + help = "Provide a custom path to the configuration file. The configuration file must be a toml file.", parse(from_os_str) )] - pub data_dir: Option, + pub config: Option, #[structopt(subcommand)] pub cmd: Command, @@ -31,9 +31,6 @@ pub enum Command { #[structopt(long = "receive-xmr", help = "Monero amount as floating point nr without denomination (e.g. 125.1)", parse(try_from_str = parse_xmr))] receive_monero: monero::Amount, - - #[structopt(flatten)] - config: Config, }, History, Resume(Resume), @@ -52,9 +49,6 @@ pub enum Resume { #[structopt(long = "counterpart-addr")] alice_addr: Multiaddr, - - #[structopt(flatten)] - config: Config, }, } @@ -71,9 +65,6 @@ pub enum Cancel { #[structopt(long = "counterpart-addr")] alice_addr: Multiaddr, - #[structopt(flatten)] - config: Config, - #[structopt(short, long)] force: bool, }, @@ -92,24 +83,11 @@ pub enum Refund { #[structopt(long = "counterpart-addr")] alice_addr: Multiaddr, - #[structopt(flatten)] - config: Config, - #[structopt(short, long)] force: bool, }, } -#[derive(structopt::StructOpt, Debug)] -pub struct Config { - #[structopt( - long = "config", - help = "Provide a custom path to the configuration file. The configuration file must be a toml file.", - parse(from_os_str) - )] - pub path: Option, -} - fn parse_btc(str: &str) -> anyhow::Result { let amount = bitcoin::Amount::from_str_in(str, ::bitcoin::Denomination::Bitcoin)?; Ok(amount) diff --git a/swap/src/cli/config.rs b/swap/src/cli/config.rs index 1f7f95fb..8dd15741 100644 --- a/swap/src/cli/config.rs +++ b/swap/src/cli/config.rs @@ -1,4 +1,4 @@ -use crate::fs::ensure_directory_exists; +use crate::fs::{default_data_dir, ensure_directory_exists}; use anyhow::{Context, Result}; use config::ConfigError; use dialoguer::{theme::ColorfulTheme, Input}; @@ -16,6 +16,7 @@ const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] pub struct Config { + pub data: Data, pub bitcoin: Bitcoin, pub monero: Monero, } @@ -33,6 +34,12 @@ impl Config { } } +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Data { + pub dir: PathBuf, +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Bitcoin { @@ -86,6 +93,18 @@ where pub fn query_user_for_initial_testnet_config() -> Result { println!(); + let data_dir = Input::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter data directory for the swap CLI or hit return to use default") + .default( + default_data_dir() + .context("No default data dir value for this system")? + .to_str() + .context("Unsupported characters in default path")? + .to_string(), + ) + .interact_text()?; + let data_dir = data_dir.as_str().parse()?; + let bitcoind_url = Input::with_theme(&ColorfulTheme::default()) .with_prompt("Enter Bitcoind URL (including username and password if applicable) or hit return to use default") .default(DEFAULT_BITCOIND_TESTNET_URL.to_owned()) @@ -104,6 +123,7 @@ pub fn query_user_for_initial_testnet_config() -> Result { println!(); Ok(Config { + data: Data { dir: data_dir }, bitcoin: Bitcoin { bitcoind_url, wallet_name: bitcoin_wallet_name, @@ -126,6 +146,9 @@ mod tests { let config_path = Path::join(&temp_dir, "config.toml"); let expected = Config { + data: Data { + dir: Default::default(), + }, bitcoin: Bitcoin { bitcoind_url: Url::from_str("http://127.0.0.1:18332").unwrap(), wallet_name: "alice".to_string(), diff --git a/swap/src/protocol/bob.rs b/swap/src/protocol/bob.rs index d3010801..8f32daf0 100644 --- a/swap/src/protocol/bob.rs +++ b/swap/src/protocol/bob.rs @@ -15,7 +15,7 @@ use crate::{ use anyhow::{bail, Error, Result}; use libp2p::{core::Multiaddr, identity::Keypair, NetworkBehaviour, PeerId}; use rand::rngs::OsRng; -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use tracing::{debug, info}; use uuid::Uuid; @@ -55,7 +55,7 @@ pub struct Builder { swap_id: Uuid, identity: Keypair, peer_id: PeerId, - db_path: PathBuf, + db: Database, alice_address: Multiaddr, alice_peer_id: PeerId, @@ -76,7 +76,7 @@ impl Builder { #[allow(clippy::too_many_arguments)] pub fn new( seed: Seed, - db_path: PathBuf, + db: Database, swap_id: Uuid, bitcoin_wallet: Arc, monero_wallet: Arc, @@ -91,7 +91,7 @@ impl Builder { swap_id, identity, peer_id, - db_path, + db, alice_address, alice_peer_id, bitcoin_wallet, @@ -117,13 +117,11 @@ impl Builder { let (event_loop, event_loop_handle) = self.init_event_loop()?; - let db = Database::open(self.db_path.as_path())?; - Ok(( Swap { state: initial_state, event_loop_handle, - db, + db: self.db, bitcoin_wallet: self.bitcoin_wallet.clone(), monero_wallet: self.monero_wallet.clone(), swap_id: self.swap_id, @@ -134,17 +132,15 @@ impl Builder { } InitParams::None => { - // reopen the existing database - let db = Database::open(self.db_path.as_path())?; - - let resume_state = if let database::Swap::Bob(state) = db.get_state(self.swap_id)? { - state.into() - } else { - bail!( - "Trying to load swap with id {} for the wrong direction.", - self.swap_id - ) - }; + let resume_state = + if let database::Swap::Bob(state) = self.db.get_state(self.swap_id)? { + state.into() + } else { + bail!( + "Trying to load swap with id {} for the wrong direction.", + self.swap_id + ) + }; let (event_loop, event_loop_handle) = self.init_event_loop()?; @@ -152,7 +148,7 @@ impl Builder { Swap { state: resume_state, event_loop_handle, - db, + db: self.db, bitcoin_wallet: self.bitcoin_wallet.clone(), monero_wallet: self.monero_wallet.clone(), swap_id: self.swap_id, diff --git a/swap/tests/testutils/mod.rs b/swap/tests/testutils/mod.rs index bec889af..b18bf57f 100644 --- a/swap/tests/testutils/mod.rs +++ b/swap/tests/testutils/mod.rs @@ -51,7 +51,7 @@ impl BobParams { pub fn builder(&self) -> bob::Builder { bob::Builder::new( self.seed, - self.db_path.clone(), + Database::open(&self.db_path.clone().as_path()).unwrap(), self.swap_id, self.bitcoin_wallet.clone(), self.monero_wallet.clone(),