Configuration for RPC urls and Bitcoin wallet name

pull/161/head
Daniel Karzel 3 years ago
parent 9eae0db9ac
commit 802dc61e7e

541
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -14,10 +14,13 @@ backoff = { git = "https://github.com/ihrwein/backoff", rev = "9d03992a83dfdc596
base64 = "0.12"
bitcoin = { version = "0.25", features = ["rand", "use-serde"] }
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "ae2f6cd547496e680941c0910018bbe884128799" }
config = { version = "0.10", default-features = false, features = ["toml"] }
conquer-once = "0.3"
cross-curve-dleq = { git = "https://github.com/comit-network/cross-curve-dleq", rev = "eddcdea1d1f16fa33ef581d1744014ece535c920", features = ["serde"] }
curve25519-dalek = "2"
derivative = "2"
dialoguer = "0.7"
directories-next = "2"
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", rev = "cdfbc766045ea678a41780919d6228dd5acee3be", features = ["libsecp_compat", "serde"] }
ed25519-dalek = { version = "1.0.0-pre.4", features = ["serde"] }# Cannot be 1 because they depend on curve25519-dalek version 3
futures = { version = "0.3", default-features = false }
@ -43,12 +46,13 @@ tempfile = "3"
thiserror = "1"
time = "0.2"
tokio = { version = "1.0", features = ["rt-multi-thread", "time", "macros", "sync"] }
toml = "0.5"
tracing = { version = "0.1", features = ["attributes"] }
tracing-core = "0.1"
tracing-futures = { version = "0.2", features = ["std-future", "futures-03"] }
tracing-log = "0.1"
tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter"] }
url = "2.1"
url = { version = "2.1", features = ["serde"] }
uuid = { version = "0.8", features = ["serde", "v4"] }
void = "1"

@ -10,7 +10,7 @@ pub use ::bitcoin::{util::amount::Amount, Address, Network, Transaction, Txid};
pub use ecdsa_fun::{adaptor::EncryptedSignature, fun::Scalar, Signature};
pub use wallet::Wallet;
use crate::{bitcoin::timelocks::BlockHeight, config::Config};
use crate::{bitcoin::timelocks::BlockHeight, config::ExecutionParams};
use ::bitcoin::{
hashes::{hex::ToHex, Hash},
secp256k1,
@ -208,7 +208,11 @@ pub trait WatchForRawTransaction {
#[async_trait]
pub trait WaitForTransactionFinality {
async fn wait_for_transaction_finality(&self, txid: Txid, config: Config) -> Result<()>;
async fn wait_for_transaction_finality(
&self,
txid: Txid,
execution_params: ExecutionParams,
) -> Result<()>;
}
#[async_trait]

@ -4,7 +4,7 @@ use crate::{
GetBlockHeight, GetNetwork, GetRawTransaction, SignTxLock, Transaction,
TransactionBlockHeight, TxLock, WaitForTransactionFinality, WatchForRawTransaction,
},
config::Config,
config::ExecutionParams,
};
use ::bitcoin::{util::psbt::PartiallySignedTransaction, Txid};
use anyhow::{Context, Result};
@ -171,17 +171,21 @@ impl TransactionBlockHeight for Wallet {
#[async_trait]
impl WaitForTransactionFinality for Wallet {
async fn wait_for_transaction_finality(&self, txid: Txid, config: Config) -> Result<()> {
async fn wait_for_transaction_finality(
&self,
txid: Txid,
execution_params: ExecutionParams,
) -> Result<()> {
// TODO(Franck): This assumes that bitcoind runs with txindex=1
// Divide by 4 to not check too often yet still be aware of the new block early
// on.
let mut interval = interval(config.bitcoin_avg_block_time / 4);
let mut interval = interval(execution_params.bitcoin_avg_block_time / 4);
loop {
let tx = self.inner.client.get_raw_transaction_verbose(txid).await?;
if let Some(confirmations) = tx.confirmations {
if confirmations >= config.bitcoin_finality_confirmations {
if confirmations >= execution_params.bitcoin_finality_confirmations {
break;
}
}

@ -1,13 +1,16 @@
use crate::{bitcoin, monero};
use libp2p::{core::Multiaddr, PeerId};
use url::Url;
use std::path::PathBuf;
use uuid::Uuid;
#[derive(structopt::StructOpt, Debug)]
pub struct Options {
// TODO: Default value should points to proper configuration folder in home folder
#[structopt(long = "data-dir", default_value = "./.swap-data/")]
pub data_dir: String,
#[structopt(
long = "data-dir",
help = "Provide a custom path to the data directory.",
parse(from_os_str)
)]
pub data_dir: Option<PathBuf>,
#[structopt(subcommand)]
pub cmd: Command,
@ -17,18 +20,6 @@ pub struct Options {
#[structopt(name = "xmr_btc-swap", about = "XMR BTC atomic swap")]
pub enum Command {
SellXmr {
#[structopt(long = "bitcoind-rpc", default_value = "http://127.0.0.1:8332")]
bitcoind_url: Url,
#[structopt(long = "bitcoin-wallet-name")]
bitcoin_wallet_name: String,
#[structopt(
long = "monero-wallet-rpc",
default_value = "http://127.0.0.1:18083/json_rpc"
)]
monero_wallet_rpc_url: Url,
#[structopt(long = "p2p-address", default_value = "/ip4/0.0.0.0/tcp/9876")]
listen_addr: Multiaddr,
@ -37,6 +28,9 @@ pub enum Command {
#[structopt(long = "receive-btc", help = "Bitcoin amount as floating point nr without denomination (e.g. 1.25)", parse(try_from_str = parse_btc))]
receive_bitcoin: bitcoin::Amount,
#[structopt(flatten)]
config: Config,
},
BuyXmr {
#[structopt(long = "connect-peer-id")]
@ -45,23 +39,14 @@ pub enum Command {
#[structopt(long = "connect-addr")]
alice_addr: Multiaddr,
#[structopt(long = "bitcoind-rpc", default_value = "http://127.0.0.1:8332")]
bitcoind_url: Url,
#[structopt(long = "bitcoin-wallet-name")]
bitcoin_wallet_name: String,
#[structopt(
long = "monero-wallet-rpc",
default_value = "http://127.0.0.1:18083/json_rpc"
)]
monero_wallet_rpc_url: Url,
#[structopt(long = "send-btc", help = "Bitcoin amount as floating point nr without denomination (e.g. 1.25)", parse(try_from_str = parse_btc))]
send_bitcoin: bitcoin::Amount,
#[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),
@ -73,20 +58,11 @@ pub enum Resume {
#[structopt(long = "swap-id")]
swap_id: Uuid,
#[structopt(long = "bitcoind-rpc", default_value = "http://127.0.0.1:8332")]
bitcoind_url: Url,
#[structopt(long = "bitcoin-wallet-name")]
bitcoin_wallet_name: String,
#[structopt(
long = "monero-wallet-rpc",
default_value = "http://127.0.0.1:18083/json_rpc"
)]
monero_wallet_rpc_url: Url,
#[structopt(long = "listen-address", default_value = "/ip4/127.0.0.1/tcp/9876")]
listen_addr: Multiaddr,
#[structopt(flatten)]
config: Config,
},
BuyXmr {
#[structopt(long = "swap-id")]
@ -98,20 +74,21 @@ pub enum Resume {
#[structopt(long = "counterpart-addr")]
alice_addr: Multiaddr,
#[structopt(long = "bitcoind-rpc", default_value = "http://127.0.0.1:8332")]
bitcoind_url: Url,
#[structopt(long = "bitcoin-wallet-name")]
bitcoin_wallet_name: String,
#[structopt(
long = "monero-wallet-rpc",
default_value = "http://127.0.0.1:18083/json_rpc"
)]
monero_wallet_rpc_url: Url,
#[structopt(flatten)]
config: Config,
},
}
#[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<PathBuf>,
}
fn parse_btc(str: &str) -> anyhow::Result<bitcoin::Amount> {
let amount = bitcoin::Amount::from_str_in(str, ::bitcoin::Denomination::Bitcoin)?;
Ok(amount)

@ -1,23 +1,19 @@
pub mod seed;
use crate::bitcoin::Timelock;
use conquer_once::Lazy;
use std::time::Duration;
#[derive(Debug, Copy, Clone)]
pub struct Config {
pub struct ExecutionParams {
pub bob_time_to_act: Duration,
pub bitcoin_finality_confirmations: u32,
pub bitcoin_avg_block_time: Duration,
pub monero_finality_confirmations: u32,
pub bitcoin_cancel_timelock: Timelock,
pub bitcoin_punish_timelock: Timelock,
pub bitcoin_network: bitcoin::Network,
pub monero_network: monero::Network,
}
pub trait GetConfig {
fn get_config() -> Config;
pub trait GetExecutionParams {
fn get_execution_params() -> ExecutionParams;
}
#[derive(Clone, Copy)]
@ -29,53 +25,47 @@ pub struct Testnet;
#[derive(Clone, Copy)]
pub struct Regtest;
impl GetConfig for Mainnet {
fn get_config() -> Config {
Config {
impl GetExecutionParams for Mainnet {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bob_time_to_act: *mainnet::BOB_TIME_TO_ACT,
bitcoin_finality_confirmations: mainnet::BITCOIN_FINALITY_CONFIRMATIONS,
bitcoin_avg_block_time: *mainnet::BITCOIN_AVG_BLOCK_TIME,
monero_finality_confirmations: mainnet::MONERO_FINALITY_CONFIRMATIONS,
bitcoin_cancel_timelock: mainnet::BITCOIN_CANCEL_TIMELOCK,
bitcoin_punish_timelock: mainnet::BITCOIN_PUNISH_TIMELOCK,
bitcoin_network: bitcoin::Network::Bitcoin,
monero_network: monero::Network::Mainnet,
}
}
}
impl GetConfig for Testnet {
fn get_config() -> Config {
Config {
impl GetExecutionParams for Testnet {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bob_time_to_act: *testnet::BOB_TIME_TO_ACT,
bitcoin_finality_confirmations: testnet::BITCOIN_FINALITY_CONFIRMATIONS,
bitcoin_avg_block_time: *testnet::BITCOIN_AVG_BLOCK_TIME,
monero_finality_confirmations: testnet::MONERO_FINALITY_CONFIRMATIONS,
bitcoin_cancel_timelock: testnet::BITCOIN_CANCEL_TIMELOCK,
bitcoin_punish_timelock: testnet::BITCOIN_PUNISH_TIMELOCK,
bitcoin_network: bitcoin::Network::Testnet,
monero_network: monero::Network::Stagenet,
}
}
}
impl GetConfig for Regtest {
fn get_config() -> Config {
Config {
impl GetExecutionParams for Regtest {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bob_time_to_act: *regtest::BOB_TIME_TO_ACT,
bitcoin_finality_confirmations: regtest::BITCOIN_FINALITY_CONFIRMATIONS,
bitcoin_avg_block_time: *regtest::BITCOIN_AVG_BLOCK_TIME,
monero_finality_confirmations: regtest::MONERO_FINALITY_CONFIRMATIONS,
bitcoin_cancel_timelock: regtest::BITCOIN_CANCEL_TIMELOCK,
bitcoin_punish_timelock: regtest::BITCOIN_PUNISH_TIMELOCK,
bitcoin_network: bitcoin::Network::Regtest,
monero_network: monero::Network::default(),
}
}
}
mod mainnet {
use super::*;
use crate::config::*;
// For each step, we are giving Bob 10 minutes to act.
pub static BOB_TIME_TO_ACT: Lazy<Duration> = Lazy::new(|| Duration::from_secs(10 * 60));
@ -92,7 +82,7 @@ mod mainnet {
}
mod testnet {
use super::*;
use crate::config::*;
pub static BOB_TIME_TO_ACT: Lazy<Duration> = Lazy::new(|| Duration::from_secs(60 * 60));
@ -110,7 +100,7 @@ mod testnet {
}
mod regtest {
use super::*;
use crate::config::*;
// In test, we set a shorter time to fail fast
pub static BOB_TIME_TO_ACT: Lazy<Duration> = Lazy::new(|| Duration::from_secs(30));

@ -0,0 +1,145 @@
use crate::fs::ensure_directory_exists;
use anyhow::{Context, Result};
use config::{Config, ConfigError};
use dialoguer::{theme::ColorfulTheme, Input};
use serde::{Deserialize, Serialize};
use std::{
ffi::OsStr,
fs,
path::{Path, PathBuf},
};
use tracing::info;
use url::Url;
pub mod seed;
const DEFAULT_BITCOIND_TESTNET_URL: &str = "http://127.0.0.1:18332";
const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc";
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct File {
pub bitcoin: Bitcoin,
pub monero: Monero,
}
impl File {
pub fn read<D>(config_file: D) -> Result<Self, ConfigError>
where
D: AsRef<OsStr>,
{
let config_file = Path::new(&config_file);
let mut config = Config::new();
config.merge(config::File::from(config_file))?;
config.try_into()
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Bitcoin {
pub bitcoind_url: Url,
pub wallet_name: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Monero {
pub wallet_rpc_url: Url,
}
#[derive(thiserror::Error, Debug, Clone, Copy)]
#[error("config not initialized")]
pub struct ConfigNotInitialized {}
pub fn read_config(config_path: PathBuf) -> Result<Result<File, ConfigNotInitialized>> {
if config_path.exists() {
info!(
"Using config file at default path: {}",
config_path.display()
);
} else {
return Ok(Err(ConfigNotInitialized {}));
}
let file = File::read(&config_path)
.with_context(|| format!("failed to read config file {}", config_path.display()))?;
Ok(Ok(file))
}
pub fn initial_setup<F>(config_path: PathBuf, config_file: F) -> Result<()>
where
F: Fn() -> Result<File>,
{
info!("Config file not found, running initial setup...");
ensure_directory_exists(config_path.as_path())?;
let initial_config = config_file()?;
let toml = toml::to_string(&initial_config)?;
fs::write(&config_path, toml)?;
info!(
"Initial setup complete, config file created at {} ",
config_path.as_path().display()
);
Ok(())
}
pub fn query_user_for_initial_testnet_config() -> Result<File> {
println!();
let bitcoind_url: String = 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())
.interact_text()?;
let bitcoind_url = Url::parse(bitcoind_url.as_str())?;
let bitcoin_wallet_name: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Enter Bitcoind wallet name")
.interact_text()?;
let monero_wallet_rpc_url: String = 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())
.interact_text()?;
let monero_wallet_rpc_url = Url::parse(monero_wallet_rpc_url.as_str())?;
println!();
Ok(File {
bitcoin: Bitcoin {
bitcoind_url,
wallet_name: bitcoin_wallet_name,
},
monero: Monero {
wallet_rpc_url: monero_wallet_rpc_url,
},
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use tempfile::tempdir;
#[test]
fn config_roundtrip() {
let temp_dir = tempdir().unwrap().path().to_path_buf();
let config_path = Path::join(&temp_dir, "config.toml");
let expected = File {
bitcoin: Bitcoin {
bitcoind_url: Url::from_str("http://127.0.0.1:18332").unwrap(),
wallet_name: "alice".to_string(),
},
monero: Monero {
wallet_rpc_url: Url::from_str("http://127.0.0.1:38083/json_rpc").unwrap(),
},
};
initial_setup(config_path.clone(), || Ok(expected.clone())).unwrap();
let actual = read_config(config_path).unwrap().unwrap();
assert_eq!(expected, actual);
}
}

@ -1,4 +1,26 @@
use std::path::Path;
use anyhow::Context;
use directories_next::ProjectDirs;
use std::path::{Path, PathBuf};
/// This is to store the configuration and seed files
// Linux: /home/<user>/.config/xmr-btc-swap/
// OSX: /Users/<user>/Library/Preferences/xmr-btc-swap/
fn default_config_dir() -> Option<PathBuf> {
ProjectDirs::from("", "", "xmr-btc-swap").map(|proj_dirs| proj_dirs.config_dir().to_path_buf())
}
pub fn default_config_path() -> anyhow::Result<PathBuf> {
default_config_dir()
.map(|dir| Path::join(&dir, "config.toml"))
.context("Could not generate default configuration path")
}
/// This is to store the DB
// Linux: /home/<user>/.local/share/nectar/
// OSX: /Users/<user>/Library/Application Support/nectar/
pub fn default_data_dir() -> Option<std::path::PathBuf> {
ProjectDirs::from("", "", "nectar").map(|proj_dirs| proj_dirs.data_dir().to_path_buf())
}
pub fn ensure_directory_exists(file: &Path) -> Result<(), std::io::Error> {
if let Some(path) = file.parent() {

@ -25,5 +25,4 @@ pub mod protocol;
pub mod seed;
pub mod trace;
mod fs;
mod serde_peer_id;

@ -12,14 +12,20 @@
#![forbid(unsafe_code)]
#![allow(non_snake_case)]
use crate::cli::{Command, Options, Resume};
use crate::{
cli::{Command, Options, Resume},
config::GetExecutionParams,
configuration::{
initial_setup, query_user_for_initial_testnet_config, read_config, ConfigNotInitialized,
},
};
use anyhow::{Context, Result};
use config::{Config, GetConfig};
use database::Database;
use fs::{default_config_path, default_data_dir};
use log::LevelFilter;
use prettytable::{row, Table};
use protocol::{alice, bob, bob::Builder, SwapAmounts};
use std::sync::Arc;
use std::{path::PathBuf, sync::Arc};
use structopt::StructOpt;
use trace::init_tracing;
use tracing::info;
@ -27,6 +33,7 @@ use uuid::Uuid;
pub mod bitcoin;
pub mod config;
pub mod configuration;
pub mod database;
pub mod monero;
pub mod network;
@ -46,40 +53,42 @@ async fn main() -> Result<()> {
init_tracing(LevelFilter::Info).expect("initialize tracing");
let opt = Options::from_args();
let config = config::Testnet::get_config();
let data_dir = if let Some(data_dir) = opt.data_dir {
data_dir
} else {
default_data_dir().context("unable to determine default data path")?
};
info!(
"Database and Seed will be stored in directory: {}",
opt.data_dir
data_dir.display()
);
let data_dir = std::path::Path::new(opt.data_dir.as_str()).to_path_buf();
let db_path = data_dir.join("database");
let seed = config::seed::Seed::from_file_or_generate(&data_dir)
let db_path = data_dir.join("database");
let seed = configuration::seed::Seed::from_file_or_generate(&data_dir)
.expect("Could not retrieve/initialize seed")
.into();
// hardcode to testnet/stagenet
let bitcoin_network = bitcoin::Network::Testnet;
let monero_network = monero::Network::Stagenet;
let execution_params = config::Testnet::get_execution_params();
match opt.cmd {
Command::SellXmr {
bitcoind_url,
bitcoin_wallet_name,
monero_wallet_rpc_url,
listen_addr,
send_monero,
receive_bitcoin,
config,
} => {
let swap_amounts = SwapAmounts {
xmr: send_monero,
btc: receive_bitcoin,
};
let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url,
bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url,
config,
)
.await?;
let (bitcoin_wallet, monero_wallet) =
init_wallets(config.path, bitcoin_network, monero_network).await?;
let swap_id = Uuid::new_v4();
@ -90,7 +99,7 @@ async fn main() -> Result<()> {
let alice_factory = alice::Builder::new(
seed,
config,
execution_params,
swap_id,
Arc::new(bitcoin_wallet),
Arc::new(monero_wallet),
@ -106,24 +115,17 @@ async fn main() -> Result<()> {
Command::BuyXmr {
alice_peer_id,
alice_addr,
bitcoind_url,
bitcoin_wallet_name,
monero_wallet_rpc_url,
send_bitcoin,
receive_monero,
config,
} => {
let swap_amounts = SwapAmounts {
btc: send_bitcoin,
xmr: receive_monero,
};
let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url,
bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url,
config,
)
.await?;
let (bitcoin_wallet, monero_wallet) =
init_wallets(config.path, bitcoin_network, monero_network).await?;
let swap_id = Uuid::new_v4();
@ -140,7 +142,7 @@ async fn main() -> Result<()> {
Arc::new(monero_wallet),
alice_addr,
alice_peer_id,
config,
execution_params,
);
let (swap, event_loop) = bob_factory.with_init_params(swap_amounts).build().await?;
@ -163,22 +165,15 @@ async fn main() -> Result<()> {
}
Command::Resume(Resume::SellXmr {
swap_id,
bitcoind_url,
bitcoin_wallet_name,
monero_wallet_rpc_url,
listen_addr,
config,
}) => {
let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url,
bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url,
config,
)
.await?;
let (bitcoin_wallet, monero_wallet) =
init_wallets(config.path, bitcoin_network, monero_network).await?;
let alice_factory = alice::Builder::new(
seed,
config,
execution_params,
swap_id,
Arc::new(bitcoin_wallet),
Arc::new(monero_wallet),
@ -192,19 +187,12 @@ async fn main() -> Result<()> {
}
Command::Resume(Resume::BuyXmr {
swap_id,
bitcoind_url,
bitcoin_wallet_name,
monero_wallet_rpc_url,
alice_peer_id,
alice_addr,
config,
}) => {
let (bitcoin_wallet, monero_wallet) = setup_wallets(
bitcoind_url,
bitcoin_wallet_name.as_str(),
monero_wallet_rpc_url,
config,
)
.await?;
let (bitcoin_wallet, monero_wallet) =
init_wallets(config.path, bitcoin_network, monero_network).await?;
let bob_factory = Builder::new(
seed,
@ -214,7 +202,7 @@ async fn main() -> Result<()> {
Arc::new(monero_wallet),
alice_addr,
alice_peer_id,
config,
execution_params,
);
let (swap, event_loop) = bob_factory.build().await?;
@ -226,21 +214,38 @@ async fn main() -> Result<()> {
Ok(())
}
async fn setup_wallets(
bitcoind_url: url::Url,
bitcoin_wallet_name: &str,
monero_wallet_rpc_url: url::Url,
config: Config,
async fn init_wallets(
config_path: Option<PathBuf>,
bitcoin_network: bitcoin::Network,
monero_network: monero::Network,
) -> Result<(bitcoin::Wallet, monero::Wallet)> {
let bitcoin_wallet =
bitcoin::Wallet::new(bitcoin_wallet_name, bitcoind_url, config.bitcoin_network).await?;
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,
bitcoin_network,
)
.await?;
let bitcoin_balance = bitcoin_wallet.balance().await?;
info!(
"Connection to Bitcoin wallet succeeded, balance: {}",
bitcoin_balance
);
let monero_wallet = monero::Wallet::new(monero_wallet_rpc_url, config.monero_network);
let monero_wallet = monero::Wallet::new(config.monero.wallet_rpc_url, monero_network);
let monero_balance = monero_wallet.get_balance().await?;
info!(
"Connection to Monero wallet succeeded, balance: {}",

@ -11,7 +11,7 @@ pub use self::{
};
use crate::{
bitcoin,
config::Config,
config::ExecutionParams,
database,
database::Database,
monero,
@ -49,7 +49,7 @@ pub struct Swap {
pub event_loop_handle: EventLoopHandle,
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
pub monero_wallet: Arc<monero::Wallet>,
pub config: Config,
pub execution_params: ExecutionParams,
pub swap_id: Uuid,
pub db: Database,
}
@ -59,7 +59,7 @@ pub struct Builder {
identity: Keypair,
peer_id: PeerId,
db_path: PathBuf,
config: Config,
execution_params: ExecutionParams,
listen_address: Multiaddr,
@ -77,7 +77,7 @@ enum InitParams {
impl Builder {
pub fn new(
seed: Seed,
config: Config,
execution_params: ExecutionParams,
swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
@ -93,7 +93,7 @@ impl Builder {
identity,
peer_id,
db_path,
config,
execution_params,
listen_address,
bitcoin_wallet,
monero_wallet,
@ -124,7 +124,7 @@ impl Builder {
event_loop_handle,
bitcoin_wallet: self.bitcoin_wallet,
monero_wallet: self.monero_wallet,
config: self.config,
execution_params: self.execution_params,
db,
state: initial_state,
swap_id: self.swap_id,
@ -154,7 +154,7 @@ impl Builder {
event_loop_handle,
bitcoin_wallet: self.bitcoin_wallet,
monero_wallet: self.monero_wallet,
config: self.config,
execution_params: self.execution_params,
swap_id: self.swap_id,
db,
},
@ -195,8 +195,8 @@ impl Builder {
v_a,
amounts.btc,
amounts.xmr,
self.config.bitcoin_cancel_timelock,
self.config.bitcoin_punish_timelock,
self.execution_params.bitcoin_cancel_timelock,
self.execution_params.bitcoin_punish_timelock,
redeem_address,
punish_address,
);

@ -7,7 +7,7 @@ use crate::{
TransactionBlockHeight, TxCancel, TxLock, TxRefund, WaitForTransactionFinality,
WatchForRawTransaction,
},
config::Config,
config::ExecutionParams,
monero,
monero::Transfer,
protocol::{
@ -33,28 +33,34 @@ pub async fn negotiate(
state0: alice::State0,
xmr_amount: monero::Amount,
event_loop_handle: &mut EventLoopHandle,
config: Config,
execution_params: ExecutionParams,
) -> Result<(PeerId, alice::State3)> {
trace!("Starting negotiate");
// todo: we can move this out, we dont need to timeout here
let bob_peer_id = timeout(
config.bob_time_to_act,
execution_params.bob_time_to_act,
event_loop_handle.recv_conn_established(),
)
.await
.context("Failed to receive dial connection from Bob")??;
let event = timeout(config.bob_time_to_act, event_loop_handle.recv_request())
.await
.context("Failed to receive swap request from Bob")??;
let event = timeout(
execution_params.bob_time_to_act,
event_loop_handle.recv_request(),
)
.await
.context("Failed to receive swap request from Bob")??;
event_loop_handle
.send_swap_response(event.channel, SwapResponse { xmr_amount })
.await?;
let (bob_message0, channel) =
timeout(config.bob_time_to_act, event_loop_handle.recv_message0()).await??;
let (bob_message0, channel) = timeout(
execution_params.bob_time_to_act,
event_loop_handle.recv_message0(),
)
.await??;
let alice_message0 = state0.next_message(&mut OsRng);
event_loop_handle
@ -63,8 +69,11 @@ pub async fn negotiate(
let state1 = state0.receive(bob_message0)?;
let (bob_message1, channel) =
timeout(config.bob_time_to_act, event_loop_handle.recv_message1()).await??;
let (bob_message1, channel) = timeout(
execution_params.bob_time_to_act,
event_loop_handle.recv_message1(),
)
.await??;
let state2 = state1.receive(bob_message1);
@ -72,7 +81,11 @@ pub async fn negotiate(
.send_message1(channel, state2.next_message())
.await?;
let bob_message2 = timeout(config.bob_time_to_act, event_loop_handle.recv_message2()).await??;
let bob_message2 = timeout(
execution_params.bob_time_to_act,
event_loop_handle.recv_message2(),
)
.await??;
let state3 = state2.receive(bob_message2)?;
@ -84,14 +97,14 @@ pub async fn negotiate(
pub async fn wait_for_locked_bitcoin<W>(
lock_bitcoin_txid: bitcoin::Txid,
bitcoin_wallet: Arc<W>,
config: Config,
execution_params: ExecutionParams,
) -> Result<()>
where
W: WatchForRawTransaction + WaitForTransactionFinality,
{
// We assume we will see Bob's transaction in the mempool first.
timeout(
config.bob_time_to_act,
execution_params.bob_time_to_act,
bitcoin_wallet.watch_for_raw_transaction(lock_bitcoin_txid),
)
.await
@ -99,7 +112,7 @@ where
// // We saw the transaction in the mempool, waiting for it to be confirmed.
bitcoin_wallet
.wait_for_transaction_finality(lock_bitcoin_txid, config)
.wait_for_transaction_finality(lock_bitcoin_txid, execution_params)
.await?;
Ok(())
@ -324,7 +337,7 @@ pub fn build_bitcoin_punish_transaction(
pub async fn publish_bitcoin_punish_transaction<W>(
punish_tx: bitcoin::Transaction,
bitcoin_wallet: Arc<W>,
config: Config,
execution_params: ExecutionParams,
) -> Result<bitcoin::Txid>
where
W: BroadcastSignedTransaction + WaitForTransactionFinality,
@ -334,7 +347,7 @@ where
.await?;
bitcoin_wallet
.wait_for_transaction_finality(txid, config)
.wait_for_transaction_finality(txid, execution_params)
.await?;
Ok(txid)

@ -6,7 +6,7 @@ use crate::{
timelocks::ExpiredTimelocks, TransactionBlockHeight, WaitForTransactionFinality,
WatchForRawTransaction,
},
config::Config,
config::ExecutionParams,
database,
database::Database,
monero,
@ -66,7 +66,7 @@ pub async fn run_until(
swap.event_loop_handle,
swap.bitcoin_wallet,
swap.monero_wallet,
swap.config,
swap.execution_params,
swap.swap_id,
swap.db,
)
@ -82,7 +82,7 @@ async fn run_until_internal(
mut event_loop_handle: EventLoopHandle,
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
config: Config,
execution_params: ExecutionParams,
swap_id: Uuid,
db: Database,
) -> Result<AliceState> {
@ -92,8 +92,13 @@ async fn run_until_internal(
} else {
match state {
AliceState::Started { amounts, state0 } => {
let (bob_peer_id, state3) =
negotiate(state0, amounts.xmr, &mut event_loop_handle, config).await?;
let (bob_peer_id, state3) = negotiate(
state0,
amounts.xmr,
&mut event_loop_handle,
execution_params,
)
.await?;
let state = AliceState::Negotiated {
bob_peer_id,
@ -110,7 +115,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet,
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -121,9 +126,12 @@ async fn run_until_internal(
bob_peer_id,
amounts,
} => {
let _ =
wait_for_locked_bitcoin(state3.tx_lock.txid(), bitcoin_wallet.clone(), config)
.await?;
let _ = wait_for_locked_bitcoin(
state3.tx_lock.txid(),
bitcoin_wallet.clone(),
execution_params,
)
.await?;
let state = AliceState::BtcLocked {
bob_peer_id,
@ -140,7 +148,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet,
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -171,7 +179,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet,
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -209,7 +217,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet.clone(),
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -235,7 +243,7 @@ async fn run_until_internal(
{
Ok(txid) => {
let publishded_redeem_tx = bitcoin_wallet
.wait_for_transaction_finality(txid, config)
.wait_for_transaction_finality(txid, execution_params)
.await;
match publishded_redeem_tx {
@ -281,7 +289,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet,
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -308,7 +316,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet,
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -342,7 +350,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet.clone(),
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -367,7 +375,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet.clone(),
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -402,7 +410,7 @@ async fn run_until_internal(
let punish_tx_finalised = publish_bitcoin_punish_transaction(
signed_tx_punish,
bitcoin_wallet.clone(),
config,
execution_params,
);
let refund_tx_seen = bitcoin_wallet.watch_for_raw_transaction(tx_refund.txid());
@ -422,7 +430,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet.clone(),
monero_wallet,
config,
execution_params,
swap_id,
db,
)
@ -446,7 +454,7 @@ async fn run_until_internal(
event_loop_handle,
bitcoin_wallet.clone(),
monero_wallet,
config,
execution_params,
swap_id,
db,
)

@ -1,9 +1,7 @@
//! Run an XMR/BTC swap in the role of Bob.
//! Bob holds BTC and wishes receive XMR.
use crate::{
bitcoin,
config::Config,
database,
bitcoin, database,
database::Database,
monero, network,
network::{
@ -30,7 +28,7 @@ pub use self::{
swap::{run, run_until},
swap_request::*,
};
use crate::protocol::alice::TransferProof;
use crate::{config::ExecutionParams, protocol::alice::TransferProof};
mod encrypted_signature;
pub mod event_loop;
@ -48,7 +46,7 @@ pub struct Swap {
pub db: Database,
pub bitcoin_wallet: Arc<bitcoin::Wallet>,
pub monero_wallet: Arc<monero::Wallet>,
pub config: Config,
pub execution_params: ExecutionParams,
pub swap_id: Uuid,
}
@ -65,7 +63,7 @@ pub struct Builder {
monero_wallet: Arc<monero::Wallet>,
init_params: InitParams,
config: Config,
execution_params: ExecutionParams,
}
enum InitParams {
@ -83,7 +81,7 @@ impl Builder {
monero_wallet: Arc<monero::Wallet>,
alice_address: Multiaddr,
alice_peer_id: PeerId,
config: Config,
execution_params: ExecutionParams,
) -> Self {
let identity = network::Seed::new(seed).derive_libp2p_identity();
let peer_id = identity.public().into_peer_id();
@ -98,7 +96,7 @@ impl Builder {
bitcoin_wallet,
monero_wallet,
init_params: InitParams::None,
config,
execution_params,
}
}
@ -113,7 +111,7 @@ impl Builder {
match self.init_params {
InitParams::New { swap_amounts } => {
let initial_state = self
.make_initial_state(swap_amounts.btc, swap_amounts.xmr, self.config)
.make_initial_state(swap_amounts.btc, swap_amounts.xmr, self.execution_params)
.await?;
let (event_loop, event_loop_handle) = self.init_event_loop()?;
@ -128,7 +126,7 @@ impl Builder {
bitcoin_wallet: self.bitcoin_wallet.clone(),
monero_wallet: self.monero_wallet.clone(),
swap_id: self.swap_id,
config: self.config,
execution_params: self.execution_params,
},
event_loop,
))
@ -157,7 +155,7 @@ impl Builder {
bitcoin_wallet: self.bitcoin_wallet.clone(),
monero_wallet: self.monero_wallet.clone(),
swap_id: self.swap_id,
config: self.config,
execution_params: self.execution_params,
},
event_loop,
))
@ -183,7 +181,7 @@ impl Builder {
&self,
btc_to_swap: bitcoin::Amount,
xmr_to_swap: monero::Amount,
config: Config,
execution_params: ExecutionParams,
) -> Result<BobState> {
let amounts = SwapAmounts {
btc: btc_to_swap,
@ -195,10 +193,10 @@ impl Builder {
&mut OsRng,
btc_to_swap,
xmr_to_swap,
config.bitcoin_cancel_timelock,
config.bitcoin_punish_timelock,
execution_params.bitcoin_cancel_timelock,
execution_params.bitcoin_punish_timelock,
refund_address,
config.monero_finality_confirmations,
execution_params.monero_finality_confirmations,
);
Ok(BobState::Started { state0, amounts })

@ -6,7 +6,7 @@ use crate::{
GetBlockHeight, GetNetwork, GetRawTransaction, Transaction, TransactionBlockHeight,
TxCancel, Txid, WatchForRawTransaction,
},
config::Config,
config::ExecutionParams,
monero,
monero::{monero_private_key, TransferProof},
protocol::{alice, bob, bob::EncryptedSignature, SwapAmounts},
@ -556,7 +556,11 @@ impl State4 {
.await
}
pub async fn refund_btc<W>(&self, bitcoin_wallet: &W, config: Config) -> Result<()>
pub async fn refund_btc<W>(
&self,
bitcoin_wallet: &W,
execution_params: ExecutionParams,
) -> Result<()>
where
W: bitcoin::BroadcastSignedTransaction + bitcoin::WaitForTransactionFinality,
{
@ -581,7 +585,7 @@ impl State4 {
.await?;
bitcoin_wallet
.wait_for_transaction_finality(txid, config)
.wait_for_transaction_finality(txid, execution_params)
.await?;
Ok(())

@ -1,7 +1,7 @@
use crate::{
bitcoin,
bitcoin::timelocks::ExpiredTimelocks,
config::Config,
config::ExecutionParams,
database::{Database, Swap},
monero,
protocol::{
@ -46,7 +46,7 @@ pub async fn run_until(
swap.monero_wallet,
OsRng,
swap.swap_id,
swap.config,
swap.execution_params,
)
.await
}
@ -63,7 +63,7 @@ async fn run_until_internal<R>(
monero_wallet: Arc<monero::Wallet>,
mut rng: R,
swap_id: Uuid,
config: Config,
execution_params: ExecutionParams,
) -> Result<BobState>
where
R: RngCore + CryptoRng + Send,
@ -97,7 +97,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -119,7 +119,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -172,7 +172,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -219,7 +219,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -262,7 +262,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -298,7 +298,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -320,7 +320,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -346,7 +346,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}
@ -357,7 +357,9 @@ where
bail!("Internal error: canceled state reached before cancel timelock was expired");
}
ExpiredTimelocks::Cancel => {
state.refund_btc(bitcoin_wallet.as_ref(), config).await?;
state
.refund_btc(bitcoin_wallet.as_ref(), execution_params)
.await?;
BobState::BtcRefunded(state)
}
ExpiredTimelocks::Punish => BobState::BtcPunished {
@ -376,7 +378,7 @@ where
monero_wallet,
rng,
swap_id,
config,
execution_params,
)
.await
}

@ -9,7 +9,7 @@ use swap::{
bitcoin,
bitcoin::Timelock,
config,
config::{Config, GetConfig},
config::{ExecutionParams, GetExecutionParams},
monero,
protocol::{alice, alice::AliceState, bob, bob::BobState, SwapAmounts},
seed::Seed,
@ -29,7 +29,7 @@ pub struct StartingBalances {
struct AliceParams {
seed: Seed,
config: Config,
execution_params: ExecutionParams,
swap_id: Uuid,
bitcoin_wallet: Arc<bitcoin::Wallet>,
monero_wallet: Arc<monero::Wallet>,
@ -41,7 +41,7 @@ impl AliceParams {
pub fn builder(&self) -> alice::Builder {
alice::Builder::new(
self.seed,
self.config,
self.execution_params,
self.swap_id,
self.bitcoin_wallet.clone(),
self.monero_wallet.clone(),
@ -64,7 +64,7 @@ struct BobParams {
monero_wallet: Arc<monero::Wallet>,
alice_address: Multiaddr,
alice_peer_id: PeerId,
config: Config,
execution_params: ExecutionParams,
}
impl BobParams {
@ -77,7 +77,7 @@ impl BobParams {
self.monero_wallet.clone(),
self.alice_address.clone(),
self.alice_peer_id,
self.config,
self.execution_params,
)
}
}
@ -308,13 +308,13 @@ pub async fn setup_test<T, F, C>(_config: C, testfn: T)
where
T: Fn(TestContext) -> F,
F: Future<Output = ()>,
C: GetConfig,
C: GetExecutionParams,
{
let cli = Cli::default();
let _guard = init_tracing();
let config = C::get_config();
let execution_params = C::get_execution_params();
let (monero, containers) = testutils::init_containers(&cli).await;
@ -334,18 +334,17 @@ where
.parse()
.expect("failed to parse Alice's address");
let (alice_bitcoin_wallet, alice_monero_wallet) = init_wallets(
let (alice_bitcoin_wallet, alice_monero_wallet) = init_test_wallets(
"alice",
&containers.bitcoind,
&monero,
alice_starting_balances.clone(),
config,
)
.await;
let alice_params = AliceParams {
seed: Seed::random().unwrap(),
config,
execution_params,
swap_id: Uuid::new_v4(),
bitcoin_wallet: alice_bitcoin_wallet.clone(),
monero_wallet: alice_monero_wallet.clone(),
@ -358,12 +357,11 @@ where
btc: swap_amounts.btc * 10,
};
let (bob_bitcoin_wallet, bob_monero_wallet) = init_wallets(
let (bob_bitcoin_wallet, bob_monero_wallet) = init_test_wallets(
"bob",
&containers.bitcoind,
&monero,
bob_starting_balances.clone(),
config,
)
.await;
@ -375,7 +373,7 @@ where
monero_wallet: bob_monero_wallet.clone(),
alice_address: alice_params.listen_address.clone(),
alice_peer_id: alice_params.peer_id(),
config,
execution_params,
};
let test = TestContext {
@ -403,12 +401,11 @@ async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
(monero, Containers { bitcoind, monerods })
}
async fn init_wallets(
async fn init_test_wallets(
name: &str,
bitcoind: &Bitcoind<'_>,
monero: &Monero,
starting_balances: StartingBalances,
config: Config,
) -> (Arc<bitcoin::Wallet>, Arc<monero::Wallet>) {
monero
.init(vec![(name, starting_balances.xmr.as_piconero())])
@ -417,11 +414,11 @@ async fn init_wallets(
let xmr_wallet = Arc::new(swap::monero::Wallet {
inner: monero.wallet(name).unwrap().client(),
network: config.monero_network,
network: monero::Network::default(),
});
let btc_wallet = Arc::new(
swap::bitcoin::Wallet::new(name, bitcoind.node_url.clone(), config.bitcoin_network)
swap::bitcoin::Wallet::new(name, bitcoind.node_url.clone(), bitcoin::Network::Regtest)
.await
.unwrap(),
);
@ -517,34 +514,34 @@ pub mod bob_run_until {
pub struct SlowCancelConfig;
impl GetConfig for SlowCancelConfig {
fn get_config() -> Config {
Config {
impl GetExecutionParams for SlowCancelConfig {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bitcoin_cancel_timelock: Timelock::new(180),
..config::Regtest::get_config()
..config::Regtest::get_execution_params()
}
}
}
pub struct FastCancelConfig;
impl GetConfig for FastCancelConfig {
fn get_config() -> Config {
Config {
impl GetExecutionParams for FastCancelConfig {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bitcoin_cancel_timelock: Timelock::new(1),
..config::Regtest::get_config()
..config::Regtest::get_execution_params()
}
}
}
pub struct FastPunishConfig;
impl GetConfig for FastPunishConfig {
fn get_config() -> Config {
Config {
impl GetExecutionParams for FastPunishConfig {
fn get_execution_params() -> ExecutionParams {
ExecutionParams {
bitcoin_cancel_timelock: Timelock::new(1),
bitcoin_punish_timelock: Timelock::new(1),
..config::Regtest::get_config()
..config::Regtest::get_execution_params()
}
}
}

Loading…
Cancel
Save