refactor(workspace): swap-fs, swap-serde, swap-env crates (#459)

* refactor: Remove monero-wallet crate

* refactor: swap-fs, swap-env, swap-serde subcrates, move dependencies into workspace root

* remove unrelated diff

* remove uncommented code

* remove unncessary diff

* merge

* refactor: remove env.rs, fix clippy error
This commit is contained in:
Mohan 2025-07-14 22:05:43 +02:00 committed by GitHub
parent e29dd4dcf2
commit bc96586acf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1074 additions and 924 deletions

936
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,26 @@
[workspace]
resolver = "2"
members = ["electrum-pool", "monero-rpc", "monero-rpc-pool", "monero-sys", "seed", "src-tauri", "swap"]
members = [ "electrum-pool", "monero-rpc", "monero-rpc-pool", "monero-sys", "seed", "src-tauri", "swap", "swap-env", "swap-fs", "swap-serde"]
[workspace.dependencies]
anyhow = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "time", "macros", "sync"] }
futures = { version = "0.3", default-features = false, features = ["std"] }
tracing = { version = "0.1", features = ["attributes"] }
bitcoin = { version = "0.32", features = ["rand", "serde"] }
monero = { version = "0.12", features = ["serde_support"] }
rand = "0.8"
uuid = { version = "1", features = ["v4"] }
typeshare = "1.0"
thiserror = "1"
reqwest = { version = "0.12", default-features = false, features = ["json"] }
rust_decimal = { version = "1", features = ["serde-float"] }
rust_decimal_macros = "1"
libp2p = { version = "0.53.2" }
url = { version = "2", features = ["serde"] }
hex = "0.4"
[patch.crates-io]
# patch until new release https://github.com/thomaseizinger/rust-jsonrpc-client/pull/51

View file

@ -7,11 +7,11 @@ edition = "2021"
[dependencies]
backoff = { version = "0.4", features = ["tokio"] }
bdk_electrum = { version = "0.19", default-features = false, features = ["use-rustls-ring"] }
bitcoin = { version = "0.32", features = ["rand", "serde"] }
futures = { version = "0.3", default-features = false, features = ["std"] }
bitcoin = { workspace = true }
futures = { workspace = true }
once_cell = "1.19"
tokio = { version = "1", features = ["rt-multi-thread", "time", "macros", "sync"] }
tracing = { version = "0.1", features = ["attributes"] }
tokio = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
serde_json = "1"
serde_json = { workspace = true }

View file

@ -6,14 +6,14 @@ edition = "2021"
publish = false
[dependencies]
anyhow = "1"
futures = "0.3"
monero = "0.12"
anyhow = { workspace = true }
futures = { workspace = true }
monero = { workspace = true }
monero-rpc = { path = "../monero-rpc" }
monero-sys = { path = "../monero-sys" }
rand = "0.7"
reqwest = "0.12.15"
rand = { workspace = true }
reqwest = { workspace = true }
testcontainers = "0.15"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "time", "macros"] }
tracing = "0.1"
tokio = { workspace = true, features = ["rt-multi-thread", "time", "macros"] }
tracing = { workspace = true }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "tracing-log"] }

View file

@ -9,27 +9,27 @@ name = "monero-rpc-pool"
path = "src/main.rs"
[dependencies]
anyhow = "1"
anyhow = { workspace = true }
axum = { version = "0.7", features = ["macros"] }
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.0", features = ["derive"] }
futures = "0.3"
monero = { version = "0.12", features = ["serde_support"] }
futures = { workspace = true }
monero = { workspace = true }
monero-rpc = { path = "../monero-rpc" }
rand = "0.8"
rand = { workspace = true }
regex = "1.0"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { workspace = true, features = ["rustls-tls"] }
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "chrono", "migrate"] }
tokio = { version = "1", features = ["full"] }
tokio = { workspace = true, features = ["full"] }
tower = "0.4"
tower-http = { version = "0.5", features = ["cors"] }
tracing = "0.1"
tracing = { workspace = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
typeshare = "1.0.3"
typeshare = { workspace = true }
url = "2.0"
uuid = { version = "1.0", features = ["v4"] }
uuid = { workspace = true }
[dev-dependencies]
tokio-test = "0.4"

View file

@ -5,19 +5,19 @@ authors = ["CoBloX Team <team@coblox.tech>"]
edition = "2021"
[dependencies]
anyhow = "1"
anyhow = { workspace = true }
curve25519-dalek = "3.1"
hex = "0.4"
jsonrpc_client = { version = "0.7", features = ["reqwest"] }
monero = "0.12"
monero = { workspace = true }
monero-epee-bin-serde = "1"
rand = "0.7"
reqwest = { version = "0.12", default-features = false, features = ["json"] }
rust_decimal = { version = "1", features = ["serde-float"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
rand = { workspace = true }
reqwest = { workspace = true }
rust_decimal = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
hex-literal = "0.4"
tokio = { version = "1", features = ["full"] }
tokio = { workspace = true, features = ["full"] }

View file

@ -4,12 +4,17 @@ version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.98"
anyhow = { workspace = true }
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
cxx = "1.0.137"
monero = { version = "0.12", features = ["serde_support"] }
tokio = { version = "1.44.2", features = ["sync", "time", "rt"] }
tracing = "0.1.41"
monero = { workspace = true }
serde = { workspace = true }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls", "macros", "chrono"] }
swap-serde = { path = "../swap-serde" }
tokio = { workspace = true, features = ["sync", "time", "rt"] }
tracing = { workspace = true }
typeshare = { workspace = true }
uuid = { workspace = true }
[build-dependencies]
cmake = "0.1.54"
@ -17,12 +22,12 @@ cxx-build = "1.0.137"
diffy = "0.4.2"
[dev-dependencies]
anyhow = "1.0.98"
futures = "0.3.31"
anyhow = { workspace = true }
futures = { workspace = true }
quickcheck = "1.0"
quickcheck_macros = "1.0"
tempfile = "3.19.1"
testcontainers = "0.15"
tokio = { version = "1.44.2", features = ["full"] }
tokio = { workspace = true, features = ["full"] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
uuid = { version = "1.16.0", features = ["v4"] }
uuid = { workspace = true }

View file

@ -1,19 +0,0 @@
[package]
name = "monero-wallet"
version = "0.1.0"
authors = ["CoBloX Team <team@coblox.tech>"]
edition = "2021"
[dependencies]
anyhow = "1"
monero = "0.12"
monero-rpc = { path = "../monero-rpc" }
rand = "0.7"
[dev-dependencies]
curve25519-dalek = "3"
monero-harness = { path = "../monero-harness" }
rand = "0.7"
testcontainers = "0.15"
tokio = { version = "1", features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }

View file

@ -17,12 +17,9 @@ workspace = true
[dependencies]
std-shims = { git = "https://github.com/serai-dex/serai", version = "^0.1.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true }
thiserror = { workspace = true, optional = true }
rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
[dev-dependencies]
@ -32,9 +29,7 @@ monero-primitives = { git = "https://github.com/serai-dex/serai", default-featur
[features]
std = [
"std-shims/std",
"thiserror",
"zeroize/std",
"rand_core/std",
]

View file

@ -15,11 +15,11 @@ crate-type = [ "lib", "staticlib" ]
tauri-build = { version = "^2.0.0", features = [ "config-json5" ] }
[dependencies]
anyhow = "1"
anyhow = { workspace = true }
monero-rpc-pool = { path = "../monero-rpc-pool" }
rustls = { version = "0.23.26", default-features = false, features = ["ring"] }
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
serde = { workspace = true }
serde_json = { workspace = true }
swap = { path = "../swap", features = [ "tauri" ] }
sysinfo = "=0.32.1"
tauri = { version = "^2.0.0", features = [ "config-json5" ] }
@ -30,8 +30,8 @@ tauri-plugin-process = "^2.0.0"
tauri-plugin-shell = "^2.0.0"
tauri-plugin-store = "^2.0.0"
tauri-plugin-updater = "^2.0.0"
tracing = "0.1"
uuid = "1.16.0"
tracing = { workspace = true }
uuid = { workspace = true }
zip = "4.0.0"
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]

24
swap-env/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "swap-env"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { workspace = true }
time = "0.3"
swap-serde = { path = "../swap-serde" }
bitcoin = { workspace = true }
monero = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true }
swap-fs = { path = "../swap-fs" }
dialoguer = "0.11"
config = { version = "0.14", default-features = false, features = ["toml"] }
libp2p = { workspace = true, features = ["serde"] }
thiserror = { workspace = true }
rust_decimal = { workspace = true }
url = { workspace = true }
toml = "0.8"
[lints]
workspace = true

View file

@ -1,5 +1,5 @@
use crate::env::{Mainnet, Testnet};
use crate::fs::{ensure_directory_exists, system_config_dir, system_data_dir};
use swap_fs::{ensure_directory_exists, system_config_dir, system_data_dir};
use anyhow::{bail, Context, Result};
use config::ConfigError;
use dialoguer::theme::ColorfulTheme;
@ -15,7 +15,7 @@ use std::str::FromStr;
use url::Url;
pub trait GetDefaults {
fn getConfigFileDefaults() -> Result<Defaults>;
fn get_config_file_defaults() -> Result<Defaults>;
}
pub struct Defaults {
@ -29,7 +29,7 @@ pub struct Defaults {
}
impl GetDefaults for Testnet {
fn getConfigFileDefaults() -> Result<Defaults> {
fn get_config_file_defaults() -> Result<Defaults> {
let defaults = Defaults {
config_path: default_asb_config_dir()?
.join("testnet")
@ -47,7 +47,7 @@ impl GetDefaults for Testnet {
}
impl GetDefaults for Mainnet {
fn getConfigFileDefaults() -> Result<Defaults> {
fn get_config_file_defaults() -> Result<Defaults> {
let defaults = Defaults {
config_path: default_asb_config_dir()?
.join("mainnet")
@ -128,104 +128,22 @@ pub struct Data {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Network {
#[serde(deserialize_with = "addr_list::deserialize")]
#[serde(deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")]
pub listen: Vec<Multiaddr>,
#[serde(default, deserialize_with = "addr_list::deserialize")]
#[serde(default, deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")]
pub rendezvous_point: Vec<Multiaddr>,
#[serde(default, deserialize_with = "addr_list::deserialize")]
#[serde(default, deserialize_with = "swap_serde::libp2p::multiaddresses::deserialize")]
pub external_addresses: Vec<Multiaddr>,
}
mod addr_list {
use libp2p::Multiaddr;
use serde::de::Unexpected;
use serde::{de, Deserialize, Deserializer};
use serde_json::Value;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Multiaddr>, D::Error>
where
D: Deserializer<'de>,
{
let s = Value::deserialize(deserializer)?;
match s {
Value::String(s) => {
let list: Result<Vec<_>, _> = s
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse().map_err(de::Error::custom))
.collect();
Ok(list?)
}
Value::Array(a) => {
let list: Result<Vec<_>, _> = a
.iter()
.map(|v| {
if let Value::String(s) = v {
s.trim().parse().map_err(de::Error::custom)
} else {
Err(de::Error::custom("expected a string"))
}
})
.collect();
Ok(list?)
}
value => Err(de::Error::invalid_type(
Unexpected::Other(&value.to_string()),
&"a string or array",
)),
}
}
}
mod electrum_urls {
use serde::de::Unexpected;
use serde::{de, Deserialize, Deserializer};
use serde_json::Value;
use url::Url;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Url>, D::Error>
where
D: Deserializer<'de>,
{
let s = Value::deserialize(deserializer)?;
match s {
Value::String(s) => {
let list: Result<Vec<_>, _> = s
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse().map_err(de::Error::custom))
.collect();
Ok(list?)
}
Value::Array(a) => {
let list: Result<Vec<_>, _> = a
.iter()
.map(|v| {
if let Value::String(s) = v {
s.trim().parse().map_err(de::Error::custom)
} else {
Err(de::Error::custom("expected a string"))
}
})
.collect();
Ok(list?)
}
value => Err(de::Error::invalid_type(
Unexpected::Other(&value.to_string()),
&"a string or array",
)),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Bitcoin {
#[serde(deserialize_with = "electrum_urls::deserialize")]
#[serde(deserialize_with = "swap_serde::electrum::urls::deserialize")]
pub electrum_rpc_urls: Vec<Url>,
pub target_block: u16,
pub finality_confirmations: Option<u32>,
#[serde(with = "crate::bitcoin::network")]
#[serde(with = "swap_serde::bitcoin::network")]
pub network: bitcoin::Network,
#[serde(default = "default_use_mempool_space_fee_estimation")]
pub use_mempool_space_fee_estimation: bool,
@ -240,7 +158,7 @@ fn default_use_mempool_space_fee_estimation() -> bool {
pub struct Monero {
pub daemon_url: Url,
pub finality_confirmations: Option<u64>,
#[serde(with = "crate::monero::network")]
#[serde(with = "swap_serde::monero::network")]
pub network: monero::Network,
#[serde(default = "default_monero_node_pool")]
pub monero_node_pool: bool,
@ -266,7 +184,7 @@ pub struct Maker {
pub max_buy_btc: bitcoin::Amount,
pub ask_spread: Decimal,
pub price_ticker_ws_url: Url,
#[serde(default, with = "crate::bitcoin::address_serde::option")]
#[serde(default, with = "swap_serde::bitcoin::address_serde::option")]
pub external_bitcoin_redeem_address: Option<bitcoin::Address>,
}
@ -318,14 +236,14 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
let bitcoin_network = bitcoin::Network::Testnet;
let monero_network = monero::Network::Stagenet;
let defaults = Testnet::getConfigFileDefaults()?;
let defaults = Testnet::get_config_file_defaults()?;
(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()?;
let defaults = Mainnet::get_config_file_defaults()?;
(bitcoin_network, monero_network, defaults)
};
@ -382,7 +300,8 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
.with_prompt(prompt)
.allow_empty(true)
.interact_text()?;
if electrum_url.as_str().is_empty() {
if electrum_url.is_empty() {
electrum_done = true;
} else if electrum_rpc_urls
.iter()
@ -500,7 +419,7 @@ mod tests {
let temp_dir = tempdir().unwrap().path().to_path_buf();
let config_path = Path::join(&temp_dir, "config.toml");
let defaults = Testnet::getConfigFileDefaults().unwrap();
let defaults = Testnet::get_config_file_defaults().unwrap();
let expected = Config {
data: Data {
@ -546,7 +465,7 @@ mod tests {
let temp_dir = tempdir().unwrap().path().to_path_buf();
let config_path = Path::join(&temp_dir, "config.toml");
let defaults = Mainnet::getConfigFileDefaults().unwrap();
let defaults = Mainnet::get_config_file_defaults().unwrap();
let expected = Config {
data: Data {
@ -592,7 +511,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap().path().to_path_buf();
let config_path = Path::join(&temp_dir, "config.toml");
let defaults = Mainnet::getConfigFileDefaults().unwrap();
let defaults = Mainnet::get_config_file_defaults().unwrap();
let dir = PathBuf::from("/tmp/dir");
std::env::set_var("ASB__DATA__DIR", dir.clone());

View file

@ -1,9 +1,8 @@
use crate::asb;
use crate::bitcoin::{CancelTimelock, PunishTimelock};
use serde::Serialize;
use std::cmp::max;
use std::time::Duration;
use time::ext::NumericalStdDuration;
use crate::config::Config as AsbConfig;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub struct Config {
@ -11,8 +10,8 @@ pub struct Config {
pub bitcoin_lock_confirmed_timeout: Duration,
pub bitcoin_finality_confirmations: u32,
pub bitcoin_avg_block_time: Duration,
pub bitcoin_cancel_timelock: CancelTimelock,
pub bitcoin_punish_timelock: PunishTimelock,
pub bitcoin_cancel_timelock: u32,
pub bitcoin_punish_timelock: u32,
pub bitcoin_network: bitcoin::Network,
pub monero_avg_block_time: Duration,
pub monero_finality_confirmations: u64,
@ -20,7 +19,7 @@ pub struct Config {
pub monero_lock_retry_timeout: Duration,
// After this many confirmations we assume that the Monero transaction is safe from double spending
pub monero_double_spend_safe_confirmations: u64,
#[serde(with = "monero_network")]
#[serde(with = "swap_serde::monero::network")]
pub monero_network: monero::Network,
}
@ -54,8 +53,8 @@ impl GetConfig for Mainnet {
bitcoin_lock_confirmed_timeout: 2.std_hours(),
bitcoin_finality_confirmations: 1,
bitcoin_avg_block_time: 10.std_minutes(),
bitcoin_cancel_timelock: CancelTimelock::new(72),
bitcoin_punish_timelock: PunishTimelock::new(144),
bitcoin_cancel_timelock: 72,
bitcoin_punish_timelock: 144,
bitcoin_network: bitcoin::Network::Bitcoin,
monero_avg_block_time: 2.std_minutes(),
// If Alice cannot lock her Monero within this timeout,
@ -75,8 +74,8 @@ impl GetConfig for Testnet {
bitcoin_lock_confirmed_timeout: 1.std_hours(),
bitcoin_finality_confirmations: 1,
bitcoin_avg_block_time: 10.std_minutes(),
bitcoin_cancel_timelock: CancelTimelock::new(12),
bitcoin_punish_timelock: PunishTimelock::new(24),
bitcoin_cancel_timelock: 12,
bitcoin_punish_timelock: 24,
bitcoin_network: bitcoin::Network::Testnet,
monero_avg_block_time: 2.std_minutes(),
monero_lock_retry_timeout: 10.std_minutes(),
@ -94,8 +93,8 @@ impl GetConfig for Regtest {
bitcoin_lock_confirmed_timeout: 5.std_minutes(),
bitcoin_finality_confirmations: 1,
bitcoin_avg_block_time: 5.std_seconds(),
bitcoin_cancel_timelock: CancelTimelock::new(100),
bitcoin_punish_timelock: PunishTimelock::new(50),
bitcoin_cancel_timelock: 100,
bitcoin_punish_timelock: 50,
bitcoin_network: bitcoin::Network::Regtest,
monero_avg_block_time: 1.std_seconds(),
monero_lock_retry_timeout: 1.std_minutes(),
@ -110,7 +109,7 @@ fn sync_interval(avg_block_time: Duration) -> Duration {
max(avg_block_time / 10, Duration::from_secs(1))
}
pub fn new(is_testnet: bool, asb_config: &asb::config::Config) -> Config {
pub fn new(is_testnet: bool, asb_config: &AsbConfig) -> Config {
let env_config = if is_testnet {
Testnet::get_config()
} else {
@ -137,22 +136,7 @@ pub fn new(is_testnet: bool, asb_config: &asb::config::Config) -> Config {
}
}
mod monero_network {
use crate::monero::Network;
use serde::Serializer;
pub fn serialize<S>(x: &monero::Network, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let str = match x {
Network::Mainnet => "mainnet",
Network::Stagenet => "stagenet",
Network::Testnet => "testnet",
};
s.serialize_str(str)
}
}
#[cfg(test)]
mod tests {

2
swap-env/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod env;
pub mod config;

12
swap-fs/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "swap-fs"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = { workspace = true }
directories-next = "2"
tracing = { workspace = true }
[lints]
workspace = true

18
swap-serde/Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "swap-serde"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { workspace = true }
monero = { workspace = true }
bitcoin = { workspace = true }
libp2p = { workspace = true }
serde_json = { workspace = true }
url = { workspace = true }
hex = { workspace = true }
anyhow = { workspace = true }
thiserror = { workspace = true }
[lints]
workspace = true

76
swap-serde/src/bitcoin.rs Normal file
View file

@ -0,0 +1,76 @@
use serde::{Deserialize, Serialize};
use bitcoin::{Network};
#[derive(Serialize, Deserialize)]
#[serde(remote = "Network")]
#[allow(non_camel_case_types)]
#[non_exhaustive]
pub enum network {
#[serde(rename = "Mainnet")]
Bitcoin,
Testnet,
Signet,
Regtest,
}
/// This module is used to serialize and deserialize bitcoin addresses
/// even though the bitcoin crate does not support it for Address<NetworkChecked>.
pub mod address_serde {
use std::str::FromStr;
use bitcoin::address::{Address, NetworkChecked, NetworkUnchecked};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(address: &Address<NetworkChecked>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
address.to_string().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Address<NetworkChecked>, D::Error>
where
D: Deserializer<'de>,
{
let unchecked: Address<NetworkUnchecked> =
Address::from_str(&String::deserialize(deserializer)?)
.map_err(serde::de::Error::custom)?;
Ok(unchecked.assume_checked())
}
/// This submodule supports Option<Address>.
pub mod option {
use super::*;
pub fn serialize<S>(
address: &Option<Address<NetworkChecked>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match address {
Some(addr) => addr.to_string().serialize(serializer),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<Address<NetworkChecked>>, D::Error>
where
D: Deserializer<'de>,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(s) => {
let unchecked: Address<NetworkUnchecked> =
Address::from_str(&s).map_err(serde::de::Error::custom)?;
Ok(Some(unchecked.assume_checked()))
}
None => Ok(None),
}
}
}
}

View file

@ -0,0 +1,40 @@
pub mod urls {
use serde::de::Unexpected;
use serde::{de, Deserialize, Deserializer};
use serde_json::Value;
use url::Url;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Url>, D::Error>
where
D: Deserializer<'de>,
{
let s = Value::deserialize(deserializer)?;
match s {
Value::String(s) => {
let list: Result<Vec<_>, _> = s
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse().map_err(de::Error::custom))
.collect();
Ok(list?)
}
Value::Array(a) => {
let list: Result<Vec<_>, _> = a
.iter()
.map(|v| {
if let Value::String(s) = v {
s.trim().parse().map_err(de::Error::custom)
} else {
Err(de::Error::custom("expected a string"))
}
})
.collect();
Ok(list?)
}
value => Err(de::Error::invalid_type(
Unexpected::Other(&value.to_string()),
&"a string or array",
)),
}
}
}

4
swap-serde/src/lib.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod monero;
pub mod bitcoin;
pub mod libp2p;
pub mod electrum;

40
swap-serde/src/libp2p.rs Normal file
View file

@ -0,0 +1,40 @@
pub mod multiaddresses {
use libp2p::Multiaddr;
use serde::de::Unexpected;
use serde::{de, Deserialize, Deserializer};
use serde_json::Value;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Multiaddr>, D::Error>
where
D: Deserializer<'de>,
{
let s = Value::deserialize(deserializer)?;
match s {
Value::String(s) => {
let list: Result<Vec<_>, _> = s
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse().map_err(de::Error::custom))
.collect();
Ok(list?)
}
Value::Array(a) => {
let list: Result<Vec<_>, _> = a
.iter()
.map(|v| {
if let Value::String(s) = v {
s.trim().parse().map_err(de::Error::custom)
} else {
Err(de::Error::custom("expected a string"))
}
})
.collect();
Ok(list?)
}
value => Err(de::Error::invalid_type(
Unexpected::Other(&value.to_string()),
&"a string or array",
)),
}
}
}

146
swap-serde/src/monero.rs Normal file
View file

@ -0,0 +1,146 @@
use monero::{Network, Amount};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
#[serde(remote = "Network")]
#[allow(non_camel_case_types)]
pub enum network {
Mainnet,
Stagenet,
Testnet,
}
pub mod private_key {
use monero::consensus::{Decodable, Encodable};
use monero::PrivateKey;
use serde::de::Visitor;
use serde::ser::Error;
use serde::{de, Deserializer, Serializer};
use std::fmt;
use std::io::Cursor;
use hex;
struct BytesVisitor;
impl Visitor<'_> for BytesVisitor {
type Value = PrivateKey;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a byte array representing a Monero private key")
}
fn visit_bytes<E>(self, s: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
let mut s = s;
PrivateKey::consensus_decode(&mut s).map_err(|err| E::custom(format!("{:?}", err)))
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = hex::decode(s).map_err(|err| E::custom(format!("{:?}", err)))?;
PrivateKey::consensus_decode(&mut bytes.as_slice())
.map_err(|err| E::custom(format!("{:?}", err)))
}
}
pub fn serialize<S>(x: &PrivateKey, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut bytes = Cursor::new(vec![]);
x.consensus_encode(&mut bytes)
.map_err(|err| S::Error::custom(format!("{:?}", err)))?;
if s.is_human_readable() {
s.serialize_str(&hex::encode(bytes.into_inner()))
} else {
s.serialize_bytes(bytes.into_inner().as_ref())
}
}
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<PrivateKey, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let key = {
if deserializer.is_human_readable() {
deserializer.deserialize_string(BytesVisitor)?
} else {
deserializer.deserialize_bytes(BytesVisitor)?
}
};
Ok(key)
}
}
pub mod amount {
use super::*;
pub fn serialize<S>(x: &Amount, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_u64(x.as_pico())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Amount, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let picos = u64::deserialize(deserializer)?;
let amount = Amount::from_pico(picos);
Ok(amount)
}
}
pub mod address {
use anyhow::{bail, Context, Result};
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,
expected_network: monero::Network,
) -> Result<monero::Address> {
if address.network != expected_network {
bail!(MoneroAddressNetworkMismatch {
expected: expected_network,
actual: address.network,
});
}
Ok(address)
}
pub fn validate_is_testnet(
address: monero::Address,
is_testnet: bool,
) -> Result<monero::Address> {
let expected_network = if is_testnet {
monero::Network::Stagenet
} else {
monero::Network::Mainnet
};
validate(address, expected_network)
}
}

View file

@ -12,7 +12,7 @@ name = "swap"
tauri = ["dep:tauri"]
[dependencies]
anyhow = "1"
anyhow = { workspace = true }
arti-client = { version = "0.25.0", features = ["static-sqlite", "tokio", "rustls", "onion-service-service"], default-features = false }
async-compression = { version = "0.3", features = ["bzip2", "tokio"] }
async-trait = "0.1"
@ -25,7 +25,7 @@ bdk_chain = { version = "0.20" }
bdk_electrum = { version = "0.19", default-features = false, features = ["use-rustls-ring"] }
bdk_wallet = { version = "1.0.0-beta.5", features = ["rusqlite", "test-utils"] }
big-bytes = "1"
bitcoin = { version = "0.32", features = ["rand", "serde"] }
bitcoin = { workspace = true }
bmrng = "0.5.2"
comfy-table = "7.1"
config = { version = "0.14", default-features = false, features = ["toml"] }
@ -34,16 +34,16 @@ curve25519-dalek = { package = "curve25519-dalek-ng", version = "4" }
data-encoding = "2.6"
derive_builder = "0.20.2"
dialoguer = "0.11"
directories-next = "2"
ecdsa_fun = { version = "0.10", default-features = false, features = ["libsecp_compat", "serde", "adaptor"] }
ed25519-dalek = "1"
electrum-pool = { path = "../electrum-pool" }
futures = { version = "0.3", default-features = false, features = ["std"] }
hex = "0.4"
libp2p = { version = "0.53.2", features = ["tcp", "yamux", "dns", "noise", "request-response", "ping", "rendezvous", "identify", "macros", "cbor", "json", "tokio", "serde", "rsa"] }
fns = "0.0.7"
futures = { workspace = true }
hex = { workspace = true }
libp2p = { workspace = true, features = ["tcp", "yamux", "dns", "noise", "request-response", "ping", "rendezvous", "identify", "macros", "cbor", "json", "tokio", "serde", "rsa"] }
libp2p-community-tor = { git = "https://github.com/umgefahren/libp2p-tor", rev = "e6b913e0f1ac1fc90b3ee4dd31b5511140c4a9af", features = ["listen-onion-service"] }
moka = { version = "0.12", features = ["sync", "future"] }
monero = { version = "0.12", features = ["serde_support"] }
monero = { workspace = true }
monero-rpc = { path = "../monero-rpc" }
monero-rpc-pool = { path = "../monero-rpc-pool" }
monero-seed = { version = "0.1.0", path = "../seed" }
@ -51,42 +51,44 @@ monero-sys = { path = "../monero-sys" }
once_cell = "1.19"
pem = "3.0"
proptest = "1"
rand = "0.8"
rand = { workspace = true }
rand_chacha = "0.3"
regex = "1.10"
reqwest = { version = "0.12", features = ["http2", "rustls-tls-native-roots", "stream", "socks"], default-features = false }
reqwest = { workspace = true, features = ["http2", "rustls-tls-native-roots", "stream", "socks"] }
rust_decimal = { version = "1", features = ["serde-float"] }
rust_decimal_macros = "1"
rustls = { version = "0.23", default-features = false, features = ["ring"] }
semver = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde = { workspace = true }
serde_cbor = "0.11"
serde_json = "1"
serde_json = { workspace = true }
serde_with = { version = "1", features = ["macros"] }
sha2 = "0.10"
swap-serde = { path = "../swap-serde" }
swap-env = { path = "../swap-env" }
sigma_fun = { version = "0.7", default-features = false, features = ["ed25519", "serde", "secp256k1", "alloc"] }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls"] }
structopt = "0.3"
strum = { version = "0.26", features = ["derive"] }
tauri = { version = "2.0", features = ["config-json5"], optional = true, default-features = false }
thiserror = "1"
thiserror = { workspace = true }
time = "0.3"
tokio = { version = "1", features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs", "net", "parking_lot", "rt"] }
tokio = { workspace = true, features = ["process", "fs", "net", "parking_lot", "rt"] }
tokio-tungstenite = { version = "0.15", features = ["rustls-tls"] }
tokio-util = { version = "0.7", features = ["io", "codec", "rt"] }
toml = "0.8"
tor-rtcompat = { version = "0.25.0", features = ["tokio"] }
tower = { version = "0.4.13", features = ["full"] }
tower-http = { version = "0.3.4", features = ["full"] }
tracing = { version = "0.1", features = ["attributes"] }
tracing = { workspace = true }
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "time", "tracing-log", "json"] }
typeshare = "1.0.3"
typeshare = { workspace = true }
unsigned-varint = { version = "0.8.0", features = ["codec", "asynchronous_codec"] }
url = { version = "2", features = ["serde"] }
uuid = { version = "1.9", features = ["serde", "v4"] }
url = { workspace = true }
uuid = { workspace = true, features = ["serde"] }
void = "1"
zeroize = "1.8.1"
swap-fs = { path = "../swap-fs" }
[target.'cfg(not(windows))'.dependencies]
tokio-tar = "0.3"
@ -106,5 +108,5 @@ tempfile = "3"
testcontainers = "0.15"
[build-dependencies]
anyhow = "1"
anyhow = { workspace = true }
vergen = { version = "8.3", default-features = false, features = ["build", "git", "git2"] }

View file

@ -1,5 +1,4 @@
pub mod command;
pub mod config;
mod event_loop;
mod network;
mod rate;

View file

@ -1,7 +1,7 @@
use crate::asb::config::GetDefaults;
use swap_env::config::GetDefaults;
use crate::bitcoin::{bitcoin_address, Amount};
use crate::env;
use crate::env::GetConfig;
use swap_env::env;
use swap_env::env::GetConfig;
use anyhow::Result;
use bitcoin::address::NetworkUnchecked;
use bitcoin::Address;
@ -163,9 +163,9 @@ fn config_path(config: Option<PathBuf>, is_testnet: bool) -> Result<PathBuf> {
let config_path = if let Some(config_path) = config {
config_path
} else if is_testnet {
env::Testnet::getConfigFileDefaults()?.config_path
env::Testnet::get_config_file_defaults()?.config_path
} else {
env::Mainnet::getConfigFileDefaults()?.config_path
env::Mainnet::get_config_file_defaults()?.config_path
};
Ok(config_path)
@ -182,9 +182,9 @@ fn env_config(is_testnet: bool) -> env::Config {
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, 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")]
#[serde(with = "swap_serde::bitcoin::network")]
expected: bitcoin::Network,
#[serde(with = "crate::bitcoin::network")]
#[serde(with = "swap_serde::bitcoin::network")]
actual: bitcoin::Network,
}
@ -402,7 +402,7 @@ mod tests {
#[test]
fn ensure_start_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![BINARY_NAME, "start"];
@ -420,7 +420,7 @@ mod tests {
#[test]
fn ensure_history_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![BINARY_NAME, "history"];
@ -440,7 +440,7 @@ mod tests {
#[test]
fn ensure_balance_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![BINARY_NAME, "balance"];
@ -458,7 +458,7 @@ mod tests {
#[test]
fn ensure_withdraw_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![
BINARY_NAME,
@ -484,7 +484,7 @@ mod tests {
#[test]
fn ensure_cancel_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![
@ -510,7 +510,7 @@ mod tests {
#[test]
fn ensure_refund_command_mappin_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![
@ -536,7 +536,7 @@ mod tests {
#[test]
fn ensure_punish_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![
@ -562,7 +562,7 @@ mod tests {
#[test]
fn ensure_safely_abort_command_mapping_mainnet() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![
@ -588,7 +588,7 @@ mod tests {
#[test]
fn ensure_start_command_mapping_for_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![BINARY_NAME, "--testnet", "start"];
@ -606,7 +606,7 @@ mod tests {
#[test]
fn ensure_history_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![BINARY_NAME, "--testnet", "history"];
@ -626,7 +626,7 @@ mod tests {
#[test]
fn ensure_balance_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![BINARY_NAME, "--testnet", "balance"];
@ -644,7 +644,7 @@ mod tests {
#[test]
fn ensure_export_monero_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![BINARY_NAME, "--testnet", "export-monero-wallet"];
@ -663,7 +663,7 @@ mod tests {
#[test]
fn ensure_withdraw_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![
@ -690,7 +690,7 @@ mod tests {
}
#[test]
fn ensure_cancel_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![
@ -717,7 +717,7 @@ mod tests {
#[test]
fn ensure_refund_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![
@ -744,7 +744,7 @@ mod tests {
#[test]
fn ensure_punish_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![
@ -771,7 +771,7 @@ mod tests {
#[test]
fn ensure_safely_abort_command_mapping_testnet() {
let default_testnet_conf_path = env::Testnet::getConfigFileDefaults().unwrap().config_path;
let default_testnet_conf_path = env::Testnet::get_config_file_defaults().unwrap().config_path;
let testnet_env_config = env::Testnet::get_config();
let raw_ars = vec![
@ -798,7 +798,7 @@ mod tests {
#[test]
fn ensure_disable_timestamp_mapping() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![BINARY_NAME, "--disable-timestamp", "start"];
@ -816,7 +816,7 @@ mod tests {
#[test]
fn ensure_trace_mapping() {
let default_mainnet_conf_path = env::Mainnet::getConfigFileDefaults().unwrap().config_path;
let default_mainnet_conf_path = env::Mainnet::get_config_file_defaults().unwrap().config_path;
let mainnet_env_config = env::Mainnet::get_config();
let raw_ars = vec![BINARY_NAME, "--trace", "start"];

View file

@ -7,7 +7,8 @@ use crate::network::transfer_proof;
use crate::protocol::alice::swap::has_already_processed_enc_sig;
use crate::protocol::alice::{AliceState, ReservesMonero, State3, Swap};
use crate::protocol::{Database, State};
use crate::{bitcoin, env, kraken, monero};
use crate::{bitcoin, kraken, monero};
use swap_env::env;
use anyhow::{anyhow, Context, Result};
use futures::future;
use futures::future::{BoxFuture, FutureExt};

View file

@ -1,5 +1,5 @@
use crate::asb::event_loop::LatestRate;
use crate::env;
use swap_env::env;
use crate::network::quote::BidQuote;
use crate::network::rendezvous::XmrBtcNamespace;
use crate::network::swap_setup::alice;

View file

@ -24,7 +24,7 @@ use std::sync::Arc;
use structopt::clap;
use structopt::clap::ErrorKind;
use swap::asb::command::{parse_args, Arguments, Command};
use swap::asb::config::{
use swap_env::config::{
initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized,
};
use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate};
@ -444,8 +444,7 @@ pub async fn main() -> Result<()> {
async fn init_bitcoin_wallet(
config: &Config,
seed: &Seed,
env_config: swap::env::Config,
sync: bool,
env_config: swap_env::env::Config,
) -> Result<bitcoin::Wallet> {
tracing::debug!("Opening Bitcoin wallet");
let wallet = bitcoin::wallet::WalletBuilder::default()
@ -481,7 +480,7 @@ async fn init_bitcoin_wallet(
async fn init_monero_wallet(
config: &Config,
env_config: swap::env::Config,
env_config: swap_env::env::Config,
) -> Result<Arc<monero::Wallets>> {
tracing::debug!("Initializing Monero wallets");

View file

@ -42,80 +42,6 @@ use serde::{Deserialize, Serialize};
use sha2::Sha256;
use std::str::FromStr;
#[derive(Serialize, Deserialize)]
#[serde(remote = "Network")]
#[allow(non_camel_case_types)]
#[non_exhaustive]
pub enum network {
#[serde(rename = "Mainnet")]
Bitcoin,
Testnet,
Signet,
Regtest,
}
/// This module is used to serialize and deserialize bitcoin addresses
/// even though the bitcoin crate does not support it for Address<NetworkChecked>.
pub mod address_serde {
use std::str::FromStr;
use bitcoin::address::{Address, NetworkChecked, NetworkUnchecked};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(address: &Address<NetworkChecked>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
address.to_string().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Address<NetworkChecked>, D::Error>
where
D: Deserializer<'de>,
{
let unchecked: Address<NetworkUnchecked> =
Address::from_str(&String::deserialize(deserializer)?)
.map_err(serde::de::Error::custom)?;
Ok(unchecked.assume_checked())
}
/// This submodule supports Option<Address>.
pub mod option {
use super::*;
pub fn serialize<S>(
address: &Option<Address<NetworkChecked>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match address {
Some(addr) => addr.to_string().serialize(serializer),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<Address<NetworkChecked>>, D::Error>
where
D: Deserializer<'de>,
{
let opt: Option<String> = Option::deserialize(deserializer)?;
match opt {
Some(s) => {
let unchecked: Address<NetworkUnchecked> =
Address::from_str(&s).map_err(serde::de::Error::custom)?;
Ok(Some(unchecked.assume_checked()))
}
None => Ok(None),
}
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct SecretKey {
inner: Scalar,
@ -336,9 +262,9 @@ pub mod bitcoin_address {
#[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")]
#[serde(with = "swap_serde::bitcoin::network")]
expected: bitcoin::Network,
#[serde(with = "crate::bitcoin::network")]
#[serde(with = "swap_serde::bitcoin::network")]
actual: bitcoin::Network,
}
@ -555,7 +481,7 @@ pub struct NotThreeWitnesses(usize);
#[cfg(test)]
mod tests {
use super::*;
use crate::env::{GetConfig, Regtest};
use swap_env::env::{GetConfig, Regtest};
use crate::monero::TransferProof;
use crate::protocol::{alice, bob};
use bitcoin::secp256k1;

View file

@ -35,6 +35,12 @@ impl From<CancelTimelock> for u32 {
}
}
impl From<u32> for CancelTimelock {
fn from(number_of_blocks: u32) -> Self {
Self(number_of_blocks)
}
}
impl CancelTimelock {
pub const fn new(number_of_blocks: u32) -> Self {
Self(number_of_blocks)
@ -86,6 +92,12 @@ impl From<PunishTimelock> for u32 {
}
}
impl From<u32> for PunishTimelock {
fn from(number_of_blocks: u32) -> Self {
Self(number_of_blocks)
}
}
impl PunishTimelock {
pub const fn new(number_of_blocks: u32) -> Self {
Self(number_of_blocks)

View file

@ -441,7 +441,7 @@ impl Wallet {
finality_confirmations: u32,
target_block: u32,
sync_interval: Duration,
env_config: crate::env::Config,
env_config: swap_env::env::Config,
tauri_handle: Option<TauriHandle>,
) -> Result<Wallet<bdk_wallet::rusqlite::Connection, Client>> {
// Construct the private key, directory and wallet file for the new (>= 1.0.0) bdk wallet

View file

@ -5,9 +5,8 @@ use crate::cli::command::{Bitcoin, Monero};
use crate::common::tor::init_tor_client;
use crate::common::tracing_util::Format;
use crate::database::{open_db, AccessMode};
use crate::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet};
use crate::fs::system_data_dir;
use crate::monero::Wallets;
use swap_env::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet};
use swap_fs::system_data_dir;
use crate::network::rendezvous::XmrBtcNamespace;
use crate::protocol::Database;
use crate::seed::Seed;

View file

@ -155,7 +155,7 @@ pub struct WithdrawBtcArgs {
#[serde(default, with = "::bitcoin::amount::serde::as_sat::opt")]
pub amount: Option<bitcoin::Amount>,
#[typeshare(serialized_as = "string")]
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
pub address: bitcoin::Address,
}

View file

@ -7,7 +7,8 @@ use crate::network::{
cooperative_xmr_redeem_after_punish, encrypted_signature, quote, redial, transfer_proof,
};
use crate::protocol::bob::State2;
use crate::{bitcoin, env};
use crate::bitcoin;
use swap_env::env;
use anyhow::{anyhow, Error, Result};
use libp2p::request_response::{
InboundFailure, InboundRequestId, OutboundFailure, OutboundRequestId, ResponseChannel,

View file

@ -4,7 +4,7 @@ use crate::cli::api::request::{
GetHistoryArgs, ListSellersArgs, MoneroRecoveryArgs, Request, ResumeSwapArgs, WithdrawBtcArgs,
};
use crate::cli::api::Context;
use crate::monero::monero_address;
use swap_serde::monero::address;
use crate::monero::{self, MoneroAddressPool};
use anyhow::Result;
use bitcoin::address::NetworkUnchecked;
@ -69,7 +69,7 @@ where
tor,
} => {
let monero_receive_pool: MoneroAddressPool =
monero_address::validate_is_testnet(monero_receive_address, is_testnet)?.into();
swap_serde::monero::address::validate_is_testnet(monero_receive_address, is_testnet)?.into();
let bitcoin_change_address = bitcoin_change_address
.map(|address| bitcoin_address::validate(address, is_testnet))
@ -354,7 +354,7 @@ enum CliCommand {
#[structopt(long = "receive-address",
help = "The monero address where you would like to receive monero",
parse(try_from_str = monero_address::parse)
parse(try_from_str = swap_serde::monero::address::parse)
)]
monero_receive_address: monero::Address,

View file

@ -3,7 +3,7 @@ pub use bob::Bob;
pub use sqlite::SqliteDatabase;
use crate::cli::api::tauri_bindings::TauriHandle;
use crate::fs::ensure_directory_exists;
use swap_fs::ensure_directory_exists;
use crate::protocol::{Database, State};
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};

View file

@ -1,7 +1,7 @@
use crate::bitcoin::EncryptedSignature;
use crate::monero;
use crate::monero::BlockHeight;
use crate::monero::{monero_private_key, TransferProof};
use crate::monero::TransferProof;
use crate::protocol::alice;
use crate::protocol::alice::AliceState;
use serde::{Deserialize, Serialize};
@ -68,7 +68,7 @@ pub enum Alice {
monero_wallet_restore_blockheight: BlockHeight,
transfer_proof: TransferProof,
state3: alice::State3,
#[serde(with = "monero_private_key")]
#[serde(with = "swap_serde::monero::private_key")]
spend_key: monero::PrivateKey,
},
Done(AliceEndState),

View file

@ -10,7 +10,7 @@ pub enum Bob {
Started {
#[serde(with = "::bitcoin::amount::serde::as_sat")]
btc_amount: bitcoin::Amount,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
change_address: bitcoin::Address,
tx_lock_fee: bitcoin::Amount,
},

View file

@ -21,8 +21,6 @@ pub mod bitcoin;
pub mod cli;
pub mod common;
pub mod database;
pub mod env;
pub mod fs;
pub mod kraken;
pub mod libp2p_ext;
pub mod monero;

View file

@ -52,7 +52,7 @@ pub fn private_key_from_secp256k1_scalar(scalar: bitcoin::Scalar) -> PrivateKey
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct PrivateViewKey(#[serde(with = "monero_private_key")] PrivateKey);
pub struct PrivateViewKey(#[serde(with = "swap_serde::monero::private_key")] PrivateKey);
impl fmt::Display for PrivateViewKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -445,7 +445,7 @@ impl fmt::Display for Amount {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct TransferProof {
tx_hash: TxHash,
#[serde(with = "monero_private_key")]
#[serde(with = "swap_serde::monero::private_key")]
tx_key: PrivateKey,
}
@ -494,73 +494,6 @@ pub struct InsufficientFunds {
#[error("Overflow, cannot convert {0} to u64")]
pub struct OverflowError(pub String);
pub mod monero_private_key {
use monero::consensus::{Decodable, Encodable};
use monero::PrivateKey;
use serde::de::Visitor;
use serde::ser::Error;
use serde::{de, Deserializer, Serializer};
use std::fmt;
use std::io::Cursor;
struct BytesVisitor;
impl Visitor<'_> for BytesVisitor {
type Value = PrivateKey;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a byte array representing a Monero private key")
}
fn visit_bytes<E>(self, s: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
let mut s = s;
PrivateKey::consensus_decode(&mut s).map_err(|err| E::custom(format!("{:?}", err)))
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = hex::decode(s).map_err(|err| E::custom(format!("{:?}", err)))?;
PrivateKey::consensus_decode(&mut bytes.as_slice())
.map_err(|err| E::custom(format!("{:?}", err)))
}
}
pub fn serialize<S>(x: &PrivateKey, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut bytes = Cursor::new(vec![]);
x.consensus_encode(&mut bytes)
.map_err(|err| S::Error::custom(format!("{:?}", err)))?;
if s.is_human_readable() {
s.serialize_str(&hex::encode(bytes.into_inner()))
} else {
s.serialize_bytes(bytes.into_inner().as_ref())
}
}
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<PrivateKey, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let key = {
if deserializer.is_human_readable() {
deserializer.deserialize_string(BytesVisitor)?
} else {
deserializer.deserialize_bytes(BytesVisitor)?
}
};
Ok(key)
}
}
pub mod monero_amount {
use crate::monero::Amount;
use serde::{Deserialize, Deserializer, Serializer};
@ -583,52 +516,6 @@ pub mod monero_amount {
}
}
pub mod monero_address {
use anyhow::{bail, Context, Result};
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,
expected_network: monero::Network,
) -> Result<monero::Address> {
if address.network != expected_network {
bail!(MoneroAddressNetworkMismatch {
expected: expected_network,
actual: address.network,
});
}
Ok(address)
}
pub fn validate_is_testnet(
address: monero::Address,
is_testnet: bool,
) -> Result<monero::Address> {
let expected_network = if is_testnet {
monero::Network::Stagenet
} else {
monero::Network::Mainnet
};
validate(address, expected_network)
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -36,9 +36,9 @@ pub mod protocol {
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockchainNetwork {
#[serde(with = "crate::bitcoin::network")]
#[serde(with = "swap_serde::bitcoin::network")]
pub bitcoin: bitcoin::Network,
#[serde(with = "crate::monero::network")]
#[serde(with = "swap_serde::monero::network")]
pub monero: monero::Network,
}

View file

@ -5,7 +5,8 @@ use crate::network::swap_setup::{
};
use crate::protocol::alice::{State0, State3};
use crate::protocol::{Message0, Message2, Message4};
use crate::{asb, bitcoin, env, monero};
use crate::{asb, bitcoin, monero};
use swap_env::env;
use anyhow::{anyhow, Context, Result};
use futures::future::{BoxFuture, OptionFuture};
use futures::AsyncWriteExt;

View file

@ -1,7 +1,8 @@
use crate::network::swap_setup::{protocol, BlockchainNetwork, SpotPriceError, SpotPriceResponse};
use crate::protocol::bob::{State0, State2};
use crate::protocol::{Message1, Message3};
use crate::{bitcoin, cli, env, monero};
use crate::{bitcoin, cli, monero};
use swap_env::env;
use anyhow::{Context, Result};
use futures::future::{BoxFuture, OptionFuture};
use futures::AsyncWriteExt;
@ -210,8 +211,8 @@ impl ConnectionHandler for Handler {
&mut rand::thread_rng(),
new_swap_request.btc,
xmr,
env_config.bitcoin_cancel_timelock,
env_config.bitcoin_punish_timelock,
env_config.bitcoin_cancel_timelock.into(),
env_config.bitcoin_punish_timelock.into(),
new_swap_request.bitcoin_refund_address.clone(),
env_config.monero_finality_confirmations,
new_swap_request.tx_refund_fee,

View file

@ -2,7 +2,8 @@ use crate::asb::{LatestRate, RendezvousNode};
use crate::libp2p_ext::MultiAddrExt;
use crate::network::rendezvous::XmrBtcNamespace;
use crate::seed::Seed;
use crate::{asb, bitcoin, cli, env};
use crate::{asb, bitcoin, cli};
use swap_env::env;
use anyhow::Result;
use arti_client::TorClient;
use libp2p::swarm::NetworkBehaviour;

View file

@ -35,7 +35,7 @@ pub struct Message0 {
S_b_bitcoin: bitcoin::PublicKey,
dleq_proof_s_b: CrossCurveDLEQProof,
v_b: monero::PrivateViewKey,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
refund_address: bitcoin::Address,
#[serde(with = "::bitcoin::amount::serde::as_sat")]
tx_refund_fee: bitcoin::Amount,
@ -50,9 +50,9 @@ pub struct Message1 {
S_a_bitcoin: bitcoin::PublicKey,
dleq_proof_s_a: CrossCurveDLEQProof,
v_a: monero::PrivateViewKey,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
redeem_address: bitcoin::Address,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
punish_address: bitcoin::Address,
#[serde(with = "::bitcoin::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,

View file

@ -1,6 +1,6 @@
//! Run an XMR/BTC swap in the role of Alice.
//! Alice holds XMR and wishes receive BTC.
use crate::env::Config;
use swap_env::env::Config;
use crate::protocol::Database;
use crate::{asb, bitcoin, monero};
use std::sync::Arc;

View file

@ -2,7 +2,7 @@ use crate::bitcoin::{
current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
TxEarlyRefund, TxPunish, TxRedeem, TxRefund, Txid,
};
use crate::env::Config;
use swap_env::env::Config;
use crate::monero::wallet::{TransferRequest, WatchRequest};
use crate::monero::BlockHeight;
use crate::monero::TransferProof;
@ -170,8 +170,8 @@ impl State0 {
punish_address,
btc,
xmr,
cancel_timelock: env_config.bitcoin_cancel_timelock,
punish_timelock: env_config.bitcoin_punish_timelock,
cancel_timelock: env_config.bitcoin_cancel_timelock.into(),
punish_timelock: env_config.bitcoin_punish_timelock.into(),
tx_redeem_fee,
tx_punish_fee,
}
@ -413,11 +413,11 @@ pub struct State3 {
pub xmr: monero::Amount,
pub cancel_timelock: CancelTimelock,
pub punish_timelock: PunishTimelock,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
refund_address: bitcoin::Address,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
redeem_address: bitcoin::Address,
#[serde(with = "crate::bitcoin::address_serde")]
#[serde(with = "swap_serde::bitcoin::address_serde")]
punish_address: bitcoin::Address,
pub tx_lock: bitcoin::TxLock,
tx_punish_sig_bob: bitcoin::Signature,

View file

@ -7,7 +7,7 @@ use std::time::Duration;
use crate::asb::{EventLoopHandle, LatestRate};
use crate::bitcoin::ExpiredTimelocks;
use crate::common::retry;
use crate::env::Config;
use swap_env::env::Config;
use crate::monero::TransferProof;
use crate::protocol::alice::{AliceState, Swap};
use crate::{bitcoin, monero};

View file

@ -6,7 +6,8 @@ use uuid::Uuid;
use crate::cli::api::tauri_bindings::TauriHandle;
use crate::monero::MoneroAddressPool;
use crate::protocol::Database;
use crate::{bitcoin, cli, env, monero};
use crate::{bitcoin, cli, monero};
use swap_env::env;
pub use self::state::*;
pub use self::swap::{run, run_until};

View file

@ -1,4 +1,4 @@
use crate::bitcoin::address_serde;
use swap_serde::bitcoin::address_serde;
use crate::bitcoin::wallet::{EstimateFeeRate, Subscription};
use crate::bitcoin::{
self, current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, Transaction, TxCancel,
@ -6,7 +6,7 @@ use crate::bitcoin::{
};
use crate::monero::wallet::WatchRequest;
use crate::monero::{self, MoneroAddressPool, TxHash};
use crate::monero::{monero_private_key, TransferProof};
use crate::monero::{TransferProof};
use crate::monero_ext::ScalarExt;
use crate::protocol::{Message0, Message1, Message2, Message3, Message4, CROSS_CURVE_PROOF_SYSTEM};
use anyhow::{anyhow, bail, Context, Result};
@ -685,7 +685,7 @@ impl State4 {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct State5 {
#[serde(with = "monero_private_key")]
#[serde(with = "swap_serde::monero::private_key")]
s_a: monero::PrivateKey,
s_b: monero::Scalar,
v: monero::PrivateViewKey,

View file

@ -9,7 +9,8 @@ use crate::network::cooperative_xmr_redeem_after_punish::Response::{Fullfilled,
use crate::network::swap_setup::bob::NewSwap;
use crate::protocol::bob::state::*;
use crate::protocol::{bob, Database};
use crate::{bitcoin, env, monero};
use crate::{bitcoin, monero};
use swap_env::env;
use anyhow::{bail, Context as AnyContext, Result};
use std::sync::Arc;
use std::time::Duration;

View file

@ -1,5 +1,5 @@
use crate::cli::api::tauri_bindings::{SeedChoice, TauriEmitter, TauriHandle};
use crate::fs::ensure_directory_exists;
use swap_fs::ensure_directory_exists;
use ::bitcoin::bip32::Xpriv as ExtendedPrivKey;
use anyhow::{Context, Result};
use bitcoin::hashes::{sha256, Hash, HashEngine};

View file

@ -21,8 +21,8 @@ use swap::asb::FixedRate;
use swap::bitcoin::{CancelTimelock, PunishTimelock};
use swap::cli::api;
use swap::database::{AccessMode, SqliteDatabase};
use swap::env::{Config, GetConfig};
use swap::fs::ensure_directory_exists;
use swap_env::env::{Config, GetConfig};
use swap_fs::ensure_directory_exists;
use swap::monero::wallet::no_listener;
use swap::monero::Wallets;
use swap::network::rendezvous::XmrBtcNamespace;
@ -31,7 +31,8 @@ use swap::protocol::alice::{AliceState, Swap};
use swap::protocol::bob::BobState;
use swap::protocol::{alice, bob, Database};
use swap::seed::Seed;
use swap::{asb, bitcoin, cli, env, monero};
use swap::{asb, bitcoin, cli, monero};
use swap_env::env;
use tempfile::{NamedTempFile, TempDir};
use testcontainers::clients::Cli;
use testcontainers::{Container, RunnableImage};