mirror of
				https://github.com/comit-network/xmr-btc-swap.git
				synced 2025-10-30 22:49:02 -04:00 
			
		
		
		
	Activate mainnet for the ASB
To run the ASB on testnet, one actively has to provide the `--testnet` flag.
Mainnet and testnet data and config are separated into sub-folders, i.e. `{data/config-dir}/asb/testnet` and `{data-dir}/asb/mainnet`.
The initial setup is also per network. If (default) config for the network cannot be found the initial setup is triggered.
Startup includes network check to ensure the bitcoin/monero network in config file is the same as the one in the `env::Config`.
Note: Wallet initialization is done with the network set in the `env::Config`, the network saved in the config file is just to indicate what network the config file is for.
			
			
This commit is contained in:
		
							parent
							
								
									9ac5b635d7
								
							
						
					
					
						commit
						02974811ad
					
				
					 3 changed files with 181 additions and 58 deletions
				
			
		|  | @ -10,6 +10,9 @@ use uuid::Uuid; | |||
|     author | ||||
| )] | ||||
| pub struct Arguments { | ||||
|     #[structopt(long, help = "Swap on testnet")] | ||||
|     pub testnet: bool, | ||||
| 
 | ||||
|     #[structopt(
 | ||||
|         short, | ||||
|         long = "json", | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| use crate::env::{Mainnet, Testnet}; | ||||
| use crate::fs::{ensure_directory_exists, system_config_dir, system_data_dir}; | ||||
| use crate::monero; | ||||
| use crate::tor::{DEFAULT_CONTROL_PORT, DEFAULT_SOCKS5_PORT}; | ||||
| use anyhow::{bail, Context, Result}; | ||||
| use config::ConfigError; | ||||
|  | @ -11,14 +13,69 @@ use serde::{Deserialize, Serialize}; | |||
| use std::ffi::OsStr; | ||||
| use std::fs; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::str::FromStr; | ||||
| use tracing::info; | ||||
| use url::Url; | ||||
| 
 | ||||
| const DEFAULT_LISTEN_ADDRESS_TCP: &str = "/ip4/0.0.0.0/tcp/9939"; | ||||
| const DEFAULT_LISTEN_ADDRESS_WS: &str = "/ip4/0.0.0.0/tcp/9940/ws"; | ||||
| const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002"; | ||||
| const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc"; | ||||
| const DEFAULT_BITCOIN_CONFIRMATION_TARGET: usize = 3; | ||||
| pub trait GetDefaults { | ||||
|     fn getConfigFileDefaults() -> Result<Defaults>; | ||||
| } | ||||
| 
 | ||||
| pub struct Defaults { | ||||
|     pub config_path: PathBuf, | ||||
|     data_dir: PathBuf, | ||||
|     listen_address_tcp: Multiaddr, | ||||
|     listen_address_ws: Multiaddr, | ||||
|     electrum_rpc_url: Url, | ||||
|     monero_wallet_rpc_url: Url, | ||||
|     bitcoin_confirmation_target: usize, | ||||
| } | ||||
| 
 | ||||
| impl GetDefaults for Testnet { | ||||
|     fn getConfigFileDefaults() -> Result<Defaults> { | ||||
|         let defaults = Defaults { | ||||
|             config_path: system_config_dir() | ||||
|                 .map(|dir| Path::join(&dir, "asb")) | ||||
|                 .map(|dir| Path::join(&dir, "testnet")) | ||||
|                 .map(|dir| Path::join(&dir, "config.toml")) | ||||
|                 .context("Could not generate default config file path")?, | ||||
|             data_dir: system_data_dir() | ||||
|                 .map(|proj_dir| Path::join(&proj_dir, "asb")) | ||||
|                 .map(|proj_dir| Path::join(&proj_dir, "testnet")) | ||||
|                 .context("Could not generate default data dir")?, | ||||
|             listen_address_tcp: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9939")?, | ||||
|             listen_address_ws: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9940/ws")?, | ||||
|             electrum_rpc_url: Url::parse("ssl://electrum.blockstream.info:60002")?, | ||||
|             monero_wallet_rpc_url: Url::parse("http://127.0.0.1:38083/json_rpc")?, | ||||
|             bitcoin_confirmation_target: 1, | ||||
|         }; | ||||
| 
 | ||||
|         Ok(defaults) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl GetDefaults for Mainnet { | ||||
|     fn getConfigFileDefaults() -> Result<Defaults> { | ||||
|         let defaults = Defaults { | ||||
|             config_path: system_config_dir() | ||||
|                 .map(|dir| Path::join(&dir, "asb")) | ||||
|                 .map(|dir| Path::join(&dir, "mainnet")) | ||||
|                 .map(|dir| Path::join(&dir, "config.toml")) | ||||
|                 .context("Could not generate default config file path")?, | ||||
|             data_dir: system_data_dir() | ||||
|                 .map(|proj_dir| Path::join(&proj_dir, "asb")) | ||||
|                 .map(|proj_dir| Path::join(&proj_dir, "mainnet")) | ||||
|                 .context("Could not generate default data dir")?, | ||||
|             listen_address_tcp: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9939")?, | ||||
|             listen_address_ws: Multiaddr::from_str("/ip4/0.0.0.0/tcp/9940/ws")?, | ||||
|             electrum_rpc_url: Url::parse("ssl://electrum.blockstream.info:50002")?, | ||||
|             monero_wallet_rpc_url: Url::parse("http://127.0.0.1:18083/json_rpc")?, | ||||
|             bitcoin_confirmation_target: 3, | ||||
|         }; | ||||
| 
 | ||||
|         Ok(defaults) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const DEFAULT_MIN_BUY_AMOUNT: f64 = 0.002f64; | ||||
| const DEFAULT_MAX_BUY_AMOUNT: f64 = 0.02f64; | ||||
|  | @ -64,12 +121,14 @@ pub struct Network { | |||
| pub struct Bitcoin { | ||||
|     pub electrum_rpc_url: Url, | ||||
|     pub target_block: usize, | ||||
|     pub network: bitcoin::Network, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub struct Monero { | ||||
|     pub wallet_rpc_url: Url, | ||||
|     pub network: monero::Network, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] | ||||
|  | @ -118,30 +177,11 @@ pub fn read_config(config_path: PathBuf) -> Result<Result<Config, ConfigNotIniti | |||
|     Ok(Ok(file)) | ||||
| } | ||||
| 
 | ||||
| /// Default location for storing the config file for the ASB
 | ||||
| // Takes the default system config-dir and adds a `/asb/config.toml`
 | ||||
| pub fn default_config_path() -> Result<PathBuf> { | ||||
|     system_config_dir() | ||||
|         .map(|dir| Path::join(&dir, "asb")) | ||||
|         .map(|dir| Path::join(&dir, "config.toml")) | ||||
|         .context("Could not generate default config file path") | ||||
| } | ||||
| 
 | ||||
| /// Default location for storing data for the CLI
 | ||||
| // Takes the default system data-dir and adds a `/asb`
 | ||||
| fn default_data_dir() -> Result<PathBuf> { | ||||
|     system_data_dir() | ||||
|         .map(|proj_dir| Path::join(&proj_dir, "asb")) | ||||
|         .context("Could not generate default data dir") | ||||
| } | ||||
| 
 | ||||
| pub fn initial_setup<F>(config_path: PathBuf, config_file: F) -> Result<()> | ||||
| pub fn initial_setup<F>(config_path: PathBuf, config_file: F, testnet: bool) -> Result<()> | ||||
| where | ||||
|     F: Fn() -> Result<Config>, | ||||
|     F: Fn(bool) -> Result<Config>, | ||||
| { | ||||
|     info!("Config file not found, running initial setup..."); | ||||
|     let initial_config = config_file()?; | ||||
| 
 | ||||
|     let initial_config = config_file(testnet)?; | ||||
|     let toml = toml::to_string(&initial_config)?; | ||||
| 
 | ||||
|     ensure_directory_exists(config_path.as_path())?; | ||||
|  | @ -154,13 +194,30 @@ where | |||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn query_user_for_initial_testnet_config() -> Result<Config> { | ||||
| pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> { | ||||
|     let (bitcoin_network, monero_network, defaults) = if testnet { | ||||
|         tracing::info!("Running initial setup for testnet"); | ||||
| 
 | ||||
|         let bitcoin_network = bitcoin::Network::Testnet; | ||||
|         let monero_network = monero::Network::Stagenet; | ||||
|         let defaults = Testnet::getConfigFileDefaults()?; | ||||
| 
 | ||||
|         (bitcoin_network, monero_network, defaults) | ||||
|     } else { | ||||
|         tracing::info!("Running initial setup for mainnet"); | ||||
|         let bitcoin_network = bitcoin::Network::Bitcoin; | ||||
|         let monero_network = monero::Network::Mainnet; | ||||
|         let defaults = Mainnet::getConfigFileDefaults()?; | ||||
| 
 | ||||
|         (bitcoin_network, monero_network, defaults) | ||||
|     }; | ||||
| 
 | ||||
|     println!(); | ||||
|     let data_dir = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Enter data directory for asb or hit return to use default") | ||||
|         .default( | ||||
|             default_data_dir() | ||||
|                 .context("No default data dir value for this system")? | ||||
|             defaults | ||||
|                 .data_dir | ||||
|                 .to_str() | ||||
|                 .context("Unsupported characters in default path")? | ||||
|                 .to_string(), | ||||
|  | @ -170,28 +227,27 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> { | |||
| 
 | ||||
|     let target_block = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("How fast should your Bitcoin transactions be confirmed? Your transaction fee will be calculated based on this target. Hit return to use default") | ||||
|         .default(DEFAULT_BITCOIN_CONFIRMATION_TARGET) | ||||
|         .default(defaults.bitcoin_confirmation_target) | ||||
|         .interact_text()?; | ||||
| 
 | ||||
|     let listen_addresses = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Enter multiaddresses (comma separated) on which asb should list for peer-to-peer communications or hit return to use default") | ||||
|         .default( format!("{},{}", DEFAULT_LISTEN_ADDRESS_TCP, DEFAULT_LISTEN_ADDRESS_WS)) | ||||
|         .default( format!("{},{}", defaults.listen_address_tcp, defaults.listen_address_ws)) | ||||
|         .interact_text()?; | ||||
|     let listen_addresses = listen_addresses | ||||
|         .split(',') | ||||
|         .map(|str| str.parse()) | ||||
|         .collect::<Result<Vec<Multiaddr>, _>>()?; | ||||
| 
 | ||||
|     let electrum_rpc_url: String = Input::with_theme(&ColorfulTheme::default()) | ||||
|     let electrum_rpc_url: Url = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Enter Electrum RPC URL or hit return to use default") | ||||
|         .default(DEFAULT_ELECTRUM_RPC_URL.to_owned()) | ||||
|         .default(defaults.electrum_rpc_url) | ||||
|         .interact_text()?; | ||||
|     let electrum_rpc_url = Url::parse(electrum_rpc_url.as_str())?; | ||||
| 
 | ||||
|     let monero_wallet_rpc_url = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Enter Monero Wallet RPC URL or hit enter to use default") | ||||
|         .default(DEFAULT_MONERO_WALLET_RPC_TESTNET_URL.to_owned()) | ||||
|         .default(defaults.monero_wallet_rpc_url) | ||||
|         .interact_text()?; | ||||
|     let monero_wallet_rpc_url = monero_wallet_rpc_url.as_str().parse()?; | ||||
| 
 | ||||
|     let tor_control_port = Input::with_theme(&ColorfulTheme::default()) | ||||
|         .with_prompt("Enter Tor control port or hit enter to use default. If Tor is not running on your machine, no hidden service will be created.") | ||||
|  | @ -234,9 +290,11 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> { | |||
|         bitcoin: Bitcoin { | ||||
|             electrum_rpc_url, | ||||
|             target_block, | ||||
|             network: bitcoin_network, | ||||
|         }, | ||||
|         monero: Monero { | ||||
|             wallet_rpc_url: monero_wallet_rpc_url, | ||||
|             network: monero_network, | ||||
|         }, | ||||
|         tor: TorConf { | ||||
|             control_port: tor_control_port, | ||||
|  | @ -253,31 +311,31 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> { | |||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::str::FromStr; | ||||
|     use tempfile::tempdir; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn config_roundtrip() { | ||||
|     fn config_roundtrip_testnet() { | ||||
|         let temp_dir = tempdir().unwrap().path().to_path_buf(); | ||||
|         let config_path = Path::join(&temp_dir, "config.toml"); | ||||
| 
 | ||||
|         let defaults = Testnet::getConfigFileDefaults().unwrap(); | ||||
| 
 | ||||
|         let expected = Config { | ||||
|             data: Data { | ||||
|                 dir: Default::default(), | ||||
|             }, | ||||
|             bitcoin: Bitcoin { | ||||
|                 electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(), | ||||
|                 target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET, | ||||
|                 electrum_rpc_url: defaults.electrum_rpc_url, | ||||
|                 target_block: defaults.bitcoin_confirmation_target, | ||||
|                 network: bitcoin::Network::Testnet, | ||||
|             }, | ||||
|             network: Network { | ||||
|                 listen: vec![ | ||||
|                     DEFAULT_LISTEN_ADDRESS_TCP.parse().unwrap(), | ||||
|                     DEFAULT_LISTEN_ADDRESS_WS.parse().unwrap(), | ||||
|                 ], | ||||
|                 listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws], | ||||
|             }, | ||||
| 
 | ||||
|             monero: Monero { | ||||
|                 wallet_rpc_url: Url::from_str(DEFAULT_MONERO_WALLET_RPC_TESTNET_URL).unwrap(), | ||||
|                 wallet_rpc_url: defaults.monero_wallet_rpc_url, | ||||
|                 network: monero::Network::Testnet, | ||||
|             }, | ||||
|             tor: Default::default(), | ||||
|             maker: Maker { | ||||
|  | @ -287,7 +345,45 @@ mod tests { | |||
|             }, | ||||
|         }; | ||||
| 
 | ||||
|         initial_setup(config_path.clone(), || Ok(expected.clone())).unwrap(); | ||||
|         initial_setup(config_path.clone(), |_| Ok(expected.clone()), false).unwrap(); | ||||
|         let actual = read_config(config_path).unwrap().unwrap(); | ||||
| 
 | ||||
|         assert_eq!(expected, actual); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn config_roundtrip_mainnet() { | ||||
|         let temp_dir = tempdir().unwrap().path().to_path_buf(); | ||||
|         let config_path = Path::join(&temp_dir, "config.toml"); | ||||
| 
 | ||||
|         let defaults = Mainnet::getConfigFileDefaults().unwrap(); | ||||
| 
 | ||||
|         let expected = Config { | ||||
|             data: Data { | ||||
|                 dir: Default::default(), | ||||
|             }, | ||||
|             bitcoin: Bitcoin { | ||||
|                 electrum_rpc_url: defaults.electrum_rpc_url, | ||||
|                 target_block: defaults.bitcoin_confirmation_target, | ||||
|                 network: bitcoin::Network::Bitcoin, | ||||
|             }, | ||||
|             network: Network { | ||||
|                 listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws], | ||||
|             }, | ||||
| 
 | ||||
|             monero: Monero { | ||||
|                 wallet_rpc_url: defaults.monero_wallet_rpc_url, | ||||
|                 network: monero::Network::Mainnet, | ||||
|             }, | ||||
|             tor: Default::default(), | ||||
|             maker: Maker { | ||||
|                 min_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MIN_BUY_AMOUNT).unwrap(), | ||||
|                 max_buy_btc: bitcoin::Amount::from_btc(DEFAULT_MAX_BUY_AMOUNT).unwrap(), | ||||
|                 ask_spread: Decimal::from_f64(DEFAULT_SPREAD).unwrap(), | ||||
|             }, | ||||
|         }; | ||||
| 
 | ||||
|         initial_setup(config_path.clone(), |_| Ok(expected.clone()), true).unwrap(); | ||||
|         let actual = read_config(config_path).unwrap().unwrap(); | ||||
| 
 | ||||
|         assert_eq!(expected, actual); | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| #![forbid(unsafe_code)] | ||||
| #![allow(non_snake_case)] | ||||
| 
 | ||||
| use anyhow::{Context, Result}; | ||||
| use anyhow::{bail, Context, Result}; | ||||
| use libp2p::core::multiaddr::Protocol; | ||||
| use libp2p::core::Multiaddr; | ||||
| use libp2p::Swarm; | ||||
|  | @ -22,8 +22,8 @@ use std::sync::Arc; | |||
| use structopt::StructOpt; | ||||
| use swap::asb::command::{Arguments, Command, ManualRecovery, RecoverCommandParams}; | ||||
| use swap::asb::config::{ | ||||
|     default_config_path, initial_setup, query_user_for_initial_testnet_config, read_config, Config, | ||||
|     ConfigNotInitialized, | ||||
|     initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized, | ||||
|     GetDefaults, | ||||
| }; | ||||
| use swap::database::Database; | ||||
| use swap::env::GetConfig; | ||||
|  | @ -45,23 +45,49 @@ const DEFAULT_WALLET_NAME: &str = "asb-wallet"; | |||
| 
 | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<()> { | ||||
|     let opt = Arguments::from_args(); | ||||
|     asb::tracing::init(LevelFilter::DEBUG, opt.json).expect("initialize tracing"); | ||||
|     let Arguments { | ||||
|         testnet, | ||||
|         json, | ||||
|         config, | ||||
|         cmd, | ||||
|     } = Arguments::from_args(); | ||||
|     asb::tracing::init(LevelFilter::DEBUG, json).expect("initialize tracing"); | ||||
| 
 | ||||
|     let config_path = if let Some(config_path) = opt.config { | ||||
|     let config_path = if let Some(config_path) = config { | ||||
|         config_path | ||||
|     } else if testnet { | ||||
|         env::Testnet::getConfigFileDefaults()?.config_path | ||||
|     } else { | ||||
|         default_config_path()? | ||||
|         env::Mainnet::getConfigFileDefaults()?.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)?; | ||||
|             initial_setup(config_path.clone(), query_user_for_initial_config, testnet)?; | ||||
|             read_config(config_path)?.expect("after initial setup config can be read") | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let env_config = if testnet { | ||||
|         env::Testnet::get_config() | ||||
|     } else { | ||||
|         env::Mainnet::get_config() | ||||
|     }; | ||||
| 
 | ||||
|     if config.monero.network != env_config.monero_network { | ||||
|         bail!(format!( | ||||
|             "Expected monero network in config file to be {:?} but was {:?}", | ||||
|             env_config.monero_network, config.monero.network | ||||
|         )); | ||||
|     } | ||||
|     if config.bitcoin.network != env_config.bitcoin_network { | ||||
|         bail!(format!( | ||||
|             "Expected bitcoin network in config file to be {:?} but was {:?}", | ||||
|             env_config.bitcoin_network, config.bitcoin.network | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     info!( | ||||
|         db_folder = %config.data.dir.display(), | ||||
|         "Database and Seed will be stored in", | ||||
|  | @ -75,9 +101,7 @@ async fn main() -> Result<()> { | |||
|     let seed = | ||||
|         Seed::from_file_or_generate(&config.data.dir).expect("Could not retrieve/initialize seed"); | ||||
| 
 | ||||
|     let env_config = env::Testnet::get_config(); | ||||
| 
 | ||||
|     match opt.cmd { | ||||
|     match cmd { | ||||
|         Command::Start { resume_only } => { | ||||
|             let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Daniel Karzel
						Daniel Karzel