fixing tests in cli module

This commit is contained in:
Lorenzo Tucci 2022-12-04 20:50:53 +01:00
parent e1983d5639
commit f5b62f967c
No known key found for this signature in database
GPG key ID: D98C4FA2CDF590A0
7 changed files with 629 additions and 596 deletions

16
Cargo.lock generated
View file

@ -3624,6 +3624,21 @@ dependencies = [
"pest", "pest",
] ]
[[package]]
name = "sequential-macro"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb5facc5f409a55d25bf271c853402a00e1187097d326757043f5dd711944d07"
[[package]]
name = "sequential-test"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d9c0d773bc7e7733264f460e5dfa00b2510421ddd6284db0749eef8dfb79e9"
dependencies = [
"sequential-macro",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.148" version = "1.0.148"
@ -4160,6 +4175,7 @@ dependencies = [
"reqwest", "reqwest",
"rust_decimal", "rust_decimal",
"rust_decimal_macros", "rust_decimal_macros",
"sequential-test",
"serde", "serde",
"serde_cbor", "serde_cbor",
"serde_json", "serde_json",

View file

@ -86,6 +86,7 @@ serde_cbor = "0.11"
spectral = "0.6" spectral = "0.6"
tempfile = "3" tempfile = "3"
testcontainers = "0.12" testcontainers = "0.12"
sequential-test = "0.2.4"
[build-dependencies] [build-dependencies]
vergen = { version = "7", default-features = false, features = [ "git", "build" ] } vergen = { version = "7", default-features = false, features = [ "git", "build" ] }

View file

@ -2,7 +2,7 @@ use crate::bitcoin::{Amount, TxLock};
use crate::cli::command::{Bitcoin, Command, Monero, Tor}; use crate::cli::command::{Bitcoin, Command, Monero, Tor};
use crate::cli::{list_sellers, EventLoop, SellerStatus}; use crate::cli::{list_sellers, EventLoop, SellerStatus};
use crate::database::open_db; use crate::database::open_db;
use crate::env::{Config, GetConfig, Mainnet, Testnet}; use crate::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet};
use crate::fs::system_data_dir; use crate::fs::system_data_dir;
use crate::libp2p_ext::MultiAddrExt; use crate::libp2p_ext::MultiAddrExt;
use crate::network::quote::{BidQuote, ZeroQuoteReceived}; use crate::network::quote::{BidQuote, ZeroQuoteReceived};
@ -28,8 +28,11 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use std::sync::Once;
use uuid::Uuid; use uuid::Uuid;
static START: Once = Once::new();
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub struct Request { pub struct Request {
pub params: Params, pub params: Params,
@ -47,19 +50,24 @@ pub struct Params {
pub address: Option<bitcoin::Address>, pub address: Option<bitcoin::Address>,
} }
#[derive(Clone, PartialEq, Debug)]
pub struct Config {
tor_socks5_port: Option<u16>,
namespace: XmrBtcNamespace,
server_address: Option<SocketAddr>,
env_config: EnvConfig,
seed: Option<Seed>,
debug: bool,
json: bool,
is_testnet: bool,
}
pub struct Context { pub struct Context {
pub db: Arc<dyn Database + Send + Sync>, pub db: Arc<dyn Database + Send + Sync>,
bitcoin_wallet: Option<Arc<bitcoin::Wallet>>, bitcoin_wallet: Option<Arc<bitcoin::Wallet>>,
monero_wallet: Option<Arc<monero::Wallet>>, monero_wallet: Option<Arc<monero::Wallet>>,
monero_rpc_process: Option<monero::WalletRpcProcess>, monero_rpc_process: Option<monero::WalletRpcProcess>,
tor_socks5_port: Option<u16>, pub config: Config,
namespace: XmrBtcNamespace,
server_address: Option<SocketAddr>,
env_config: Config,
seed: Option<Seed>,
debug: bool,
json: bool,
is_testnet: bool,
} }
impl Request { impl Request {
@ -68,8 +76,8 @@ impl Request {
Command::BuyXmr => { Command::BuyXmr => {
let swap_id = Uuid::new_v4(); let swap_id = Uuid::new_v4();
let seed = context.seed.as_ref().unwrap(); let seed = context.config.seed.as_ref().unwrap();
let env_config = context.env_config; let env_config = context.config.env_config;
let btc = context.bitcoin_wallet.as_ref().unwrap(); let btc = context.bitcoin_wallet.as_ref().unwrap();
let seller = self.params.seller.clone().unwrap(); let seller = self.params.seller.clone().unwrap();
let monero_receive_address = self.params.monero_receive_address.unwrap(); let monero_receive_address = self.params.monero_receive_address.unwrap();
@ -92,11 +100,11 @@ impl Request {
seller_peer_id, seller_peer_id,
env_config, env_config,
bitcoin_wallet.clone(), bitcoin_wallet.clone(),
(seed.derive_libp2p_identity(), context.namespace), (seed.derive_libp2p_identity(), context.config.namespace),
); );
let mut swarm = swarm::cli( let mut swarm = swarm::cli(
seed.derive_libp2p_identity(), seed.derive_libp2p_identity(),
context.tor_socks5_port.unwrap(), context.config.tor_socks5_port.unwrap(),
behaviour, behaviour,
) )
.await?; .await?;
@ -112,7 +120,7 @@ impl Request {
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount); let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
let (amount, fees) = match determine_btc_to_swap( let (amount, fees) = match determine_btc_to_swap(
context.json, context.config.json,
event_loop_handle.request_quote(), event_loop_handle.request_quote(),
bitcoin_wallet.new_address(), bitcoin_wallet.new_address(),
|| bitcoin_wallet.balance(), || bitcoin_wallet.balance(),
@ -224,7 +232,7 @@ impl Request {
let addr2 = "127.0.0.1:1234".parse()?; let addr2 = "127.0.0.1:1234".parse()?;
let server_handle = { let server_handle = {
if let Some(addr) = context.server_address { if let Some(addr) = context.config.server_address {
let (_addr, handle) = rpc::run_server(addr, context).await?; let (_addr, handle) = rpc::run_server(addr, context).await?;
Some(handle) Some(handle)
} else { } else {
@ -257,17 +265,17 @@ impl Request {
let seller_peer_id = context.db.get_peer_id(swap_id).await?; let seller_peer_id = context.db.get_peer_id(swap_id).await?;
let seller_addresses = context.db.get_addresses(seller_peer_id).await?; let seller_addresses = context.db.get_addresses(seller_peer_id).await?;
let seed = context.seed.as_ref().unwrap().derive_libp2p_identity(); let seed = context.config.seed.as_ref().unwrap().derive_libp2p_identity();
let behaviour = cli::Behaviour::new( let behaviour = cli::Behaviour::new(
seller_peer_id, seller_peer_id,
context.env_config, context.config.env_config,
Arc::clone(context.bitcoin_wallet.as_ref().unwrap()), Arc::clone(context.bitcoin_wallet.as_ref().unwrap()),
(seed.clone(), context.namespace), (seed.clone(), context.config.namespace),
); );
let mut swarm = swarm::cli( let mut swarm = swarm::cli(
seed.clone(), seed.clone(),
context.tor_socks5_port.clone().unwrap(), context.config.tor_socks5_port.clone().unwrap(),
behaviour, behaviour,
) )
.await?; .await?;
@ -291,7 +299,7 @@ impl Request {
swap_id, swap_id,
Arc::clone(context.bitcoin_wallet.as_ref().unwrap()), Arc::clone(context.bitcoin_wallet.as_ref().unwrap()),
Arc::clone(context.monero_wallet.as_ref().unwrap()), Arc::clone(context.monero_wallet.as_ref().unwrap()),
context.env_config, context.config.env_config,
event_loop_handle, event_loop_handle,
monero_receive_address, monero_receive_address,
) )
@ -343,13 +351,13 @@ impl Request {
.extract_peer_id() .extract_peer_id()
.context("Rendezvous node address must contain peer ID")?; .context("Rendezvous node address must contain peer ID")?;
let identity = context.seed.as_ref().unwrap().derive_libp2p_identity(); let identity = context.config.seed.as_ref().unwrap().derive_libp2p_identity();
let sellers = list_sellers( let sellers = list_sellers(
rendezvous_node_peer_id, rendezvous_node_peer_id,
rendezvous_point, rendezvous_point,
context.namespace, context.config.namespace,
context.tor_socks5_port.unwrap(), context.config.tor_socks5_port.unwrap(),
identity, identity,
) )
.await?; .await?;
@ -413,7 +421,7 @@ impl Request {
let (spend_key, view_key) = state5.xmr_keys(); let (spend_key, view_key) = state5.xmr_keys();
let address = monero::Address::standard( let address = monero::Address::standard(
context.env_config.monero_network, context.config.env_config.monero_network,
monero::PublicKey::from_private_key(&spend_key), monero::PublicKey::from_private_key(&spend_key),
monero::PublicKey::from(view_key.public()), monero::PublicKey::from(view_key.public()),
); );
@ -489,21 +497,26 @@ impl Context {
} }
}; };
cli::tracing::init(debug, json, data_dir.join("logs"), None)?; START.call_once(|| {
let _ = cli::tracing::init(debug, json, data_dir.join("logs"), None);
});
let init = Context { let init = Context {
db: open_db(data_dir.join("sqlite")).await?,
bitcoin_wallet, bitcoin_wallet,
monero_wallet, monero_wallet,
monero_rpc_process, monero_rpc_process,
config: Config {
tor_socks5_port, tor_socks5_port,
namespace: XmrBtcNamespace::from_is_testnet(is_testnet), namespace: XmrBtcNamespace::from_is_testnet(is_testnet),
db: open_db(data_dir.join("sqlite")).await?,
env_config, env_config,
seed: Some(seed), seed: Some(seed),
server_address,
debug, debug,
json, json,
is_testnet, is_testnet,
server_address, },
}; };
Ok(init) Ok(init)
@ -517,22 +530,12 @@ impl Serialize for Context {
{ {
// 3 is the number of fields in the struct. // 3 is the number of fields in the struct.
let mut state = serializer.serialize_struct("Context", 3)?; let mut state = serializer.serialize_struct("Context", 3)?;
state.serialize_field("debug", &self.debug)?; state.serialize_field("debug", &self.config.debug)?;
state.serialize_field("json", &self.json)?; state.serialize_field("json", &self.config.json)?;
state.end() state.end()
} }
} }
impl PartialEq for Context {
fn eq(&self, other: &Self) -> bool {
self.tor_socks5_port == other.tor_socks5_port
&& self.namespace == other.namespace
&& self.debug == other.debug
&& self.json == other.json
&& self.server_address == other.server_address
}
}
impl fmt::Debug for Context { impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Testing {}", true) write!(f, "Testing {}", true)
@ -543,7 +546,7 @@ async fn init_bitcoin_wallet(
electrum_rpc_url: Url, electrum_rpc_url: Url,
seed: &Seed, seed: &Seed,
data_dir: PathBuf, data_dir: PathBuf,
env_config: Config, env_config: EnvConfig,
bitcoin_target_block: usize, bitcoin_target_block: usize,
) -> Result<bitcoin::Wallet> { ) -> Result<bitcoin::Wallet> {
let wallet_dir = data_dir.join("wallet"); let wallet_dir = data_dir.join("wallet");
@ -668,7 +671,7 @@ where
async fn init_monero_wallet( async fn init_monero_wallet(
data_dir: PathBuf, data_dir: PathBuf,
monero_daemon_address: String, monero_daemon_address: String,
env_config: Config, env_config: EnvConfig,
) -> Result<(monero::Wallet, monero::WalletRpcProcess)> { ) -> Result<(monero::Wallet, monero::WalletRpcProcess)> {
let network = env_config.monero_network; let network = env_config.monero_network;
@ -709,7 +712,7 @@ mod data {
} }
} }
fn env_config_from(testnet: bool) -> Config { fn env_config_from(testnet: bool) -> EnvConfig {
if testnet { if testnet {
Testnet::get_config() Testnet::get_config()
} else { } else {
@ -730,33 +733,31 @@ pub mod api_test {
pub const BITCOIN_MAINNET_ADDRESS: &str = "bc1qe4epnfklcaa0mun26yz5g8k24em5u9f92hy325"; pub const BITCOIN_MAINNET_ADDRESS: &str = "bc1qe4epnfklcaa0mun26yz5g8k24em5u9f92hy325";
pub const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b"; pub const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b";
impl Context { impl Config {
pub async fn default( pub fn default(
is_testnet: bool, is_testnet: bool,
data_dir: PathBuf, data_dir: Option<PathBuf>,
json: bool, json: bool,
debug: bool, debug: bool,
) -> Result<Context> { ) -> Self {
Ok(Context::build( let data_dir = data::data_dir_from(data_dir, is_testnet).unwrap();
Some(Bitcoin {
bitcoin_electrum_rpc_url: None, let seed = Seed::from_file_or_generate(data_dir.as_path()).unwrap();
bitcoin_target_block: None,
}), let env_config = env_config_from(is_testnet);
Some(Monero { Self {
monero_daemon_address: None, tor_socks5_port: Some(9050),
}), namespace: XmrBtcNamespace::from_is_testnet(is_testnet),
Some(Tor { server_address: None,
tor_socks5_port: DEFAULT_SOCKS5_PORT, env_config,
}), seed: Some(seed),
Some(data_dir),
is_testnet,
debug, debug,
json, json,
None, is_testnet
)
.await?)
} }
} }
}
impl Request { impl Request {
pub fn buy_xmr(is_testnet: bool) -> Request { pub fn buy_xmr(is_testnet: bool) -> Request {
let seller = Multiaddr::from_str(MULTI_ADDRESS).unwrap(); let seller = Multiaddr::from_str(MULTI_ADDRESS).unwrap();

View file

@ -15,7 +15,7 @@ pub use crate::bitcoin::refund::TxRefund;
pub use crate::bitcoin::timelocks::{BlockHeight, ExpiredTimelocks}; pub use crate::bitcoin::timelocks::{BlockHeight, ExpiredTimelocks};
pub use ::bitcoin::util::amount::Amount; pub use ::bitcoin::util::amount::Amount;
pub use ::bitcoin::util::psbt::PartiallySignedTransaction; pub use ::bitcoin::util::psbt::PartiallySignedTransaction;
pub use ::bitcoin::{Address, Network, Transaction, Txid}; pub use ::bitcoin::{Address, AddressType, Network, Transaction, Txid};
pub use ecdsa_fun::adaptor::EncryptedSignature; pub use ecdsa_fun::adaptor::EncryptedSignature;
pub use ecdsa_fun::fun::Scalar; pub use ecdsa_fun::fun::Scalar;
pub use ecdsa_fun::Signature; pub use ecdsa_fun::Signature;
@ -250,6 +250,48 @@ pub fn current_epoch(
ExpiredTimelocks::None ExpiredTimelocks::None
} }
pub mod bitcoin_address {
use anyhow::{bail, Result};
use serde::Serialize;
use std::str::FromStr;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Serialize)]
#[error("Invalid Bitcoin address provided, expected address on network {expected:?} but address provided is on {actual:?}")]
pub struct BitcoinAddressNetworkMismatch {
#[serde(with = "crate::bitcoin::network")]
expected: bitcoin::Network,
#[serde(with = "crate::bitcoin::network")]
actual: bitcoin::Network,
}
pub fn parse(addr_str: &str) -> Result<bitcoin::Address> {
let address = bitcoin::Address::from_str(addr_str)?;
if address.address_type() != Some(bitcoin::AddressType::P2wpkh) {
anyhow::bail!("Invalid Bitcoin address provided, only bech32 format is supported!")
}
Ok(address)
}
pub fn validate(address: bitcoin::Address, is_testnet: bool) -> Result<bitcoin::Address> {
let expected_network = if is_testnet {
bitcoin::Network::Testnet
} else {
bitcoin::Network::Bitcoin
};
if address.network != expected_network {
bail!(BitcoinAddressNetworkMismatch {
expected: expected_network,
actual: address.network
});
}
Ok(address)
}
}
/// Bitcoin error codes: https://github.com/bitcoin/bitcoin/blob/97d3500601c1d28642347d014a6de1e38f53ae4e/src/rpc/protocol.h#L23 /// Bitcoin error codes: https://github.com/bitcoin/bitcoin/blob/97d3500601c1d28642347d014a6de1e38f53ae4e/src/rpc/protocol.h#L23
pub enum RpcErrorCode { pub enum RpcErrorCode {
/// Transaction or block was rejected by network rules. Error code -26. /// Transaction or block was rejected by network rules. Error code -26.

File diff suppressed because it is too large Load diff

View file

@ -320,6 +320,43 @@ pub mod monero_amount {
} }
} }
pub mod monero_address {
use anyhow::{bail, Result, Context};
use std::str::FromStr;
#[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 {
pub expected: monero::Network,
pub actual: monero::Network,
}
pub fn parse(s: &str) -> Result<monero::Address> {
monero::Address::from_str(s).with_context(|| {
format!(
"Failed to parse {} as a monero address, please make sure it is a valid address",
s
)
})
}
pub fn validate(address: monero::Address, is_testnet: bool) -> Result<monero::Address> {
let expected_network = if is_testnet {
monero::Network::Stagenet
} else {
monero::Network::Mainnet
};
if address.network != expected_network {
bail!(MoneroAddressNetworkMismatch {
expected: expected_network,
actual: address.network,
});
}
Ok(address)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -16,7 +16,7 @@ use torut::onion::TorSecretKeyV3;
pub const SEED_LENGTH: usize = 32; pub const SEED_LENGTH: usize = 32;
#[derive(Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub struct Seed([u8; SEED_LENGTH]); pub struct Seed([u8; SEED_LENGTH]);
impl Seed { impl Seed {