bump: release version 2.3.0-beta.2

This commit is contained in:
Binarybaron 2025-06-24 11:57:02 +02:00
parent dc865a91f3
commit a235a537c1
21 changed files with 102 additions and 71 deletions

View file

@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.3.0-beta.2] - 2025-06-24
- ASB + GUI + CLI: We now cache fee estimates for the Bitcoin wallet for up to 2 minutes. This improves the speed of fee estimation and reduces the number of requests to the Electrum servers.
## [2.3.0-beta.1] - 2025-06-19

4
Cargo.lock generated
View file

@ -9648,7 +9648,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142"
[[package]]
name = "swap"
version = "2.3.0-beta.1"
version = "2.3.0-beta.2"
dependencies = [
"anyhow",
"arti-client",
@ -12199,7 +12199,7 @@ dependencies = [
[[package]]
name = "unstoppableswap-gui-rs"
version = "2.3.0-beta.1"
version = "2.3.0-beta.2"
dependencies = [
"anyhow",
"monero-rpc-pool",

View file

@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["monero-rpc", "monero-rpc-pool", "monero-sys", "src-tauri", "swap", "electrum-pool"]
members = ["electrum-pool", "monero-rpc", "monero-rpc-pool", "monero-sys", "src-tauri", "swap"]
[profile.release]
opt-level = 0

View file

@ -17,7 +17,7 @@ Join our [Matrix room](https://matrix.to/#/#unstoppableswap-core:matrix.org) to
## Contributing
We have a `justfile` containing a lot of useful commands.
We have a `justfile` containing a lot of useful commands.
Run `just help` to see all the available commands.
## Running tests

View file

@ -1,8 +1,8 @@
[package]
name = "electrum-pool"
version = "0.1.0"
edition = "2021"
authors = ["UnstoppableSwap Team <help@unstoppableswap.net>"]
edition = "2021"
[dependencies]
backoff = { version = "0.4", features = ["tokio"] }

View file

@ -193,11 +193,7 @@ where
/// individual failures, allowing the caller to inspect and make decisions based
/// on the specific types of errors encountered.
#[instrument(level = "debug", skip(self, f), fields(operation = kind, total_clients = self.client_count()))]
pub async fn call_async_with_multi_error<F, T>(
&self,
kind: &str,
f: F,
) -> Result<T, MultiError>
pub async fn call_async_with_multi_error<F, T>(&self, kind: &str, f: F) -> Result<T, MultiError>
where
F: Fn(&C) -> Result<T, Error> + Send + Sync + Clone + 'static,
T: Send + 'static,

View file

@ -12,9 +12,7 @@
"parameters": {
"Right": 3
},
"nullable": [
true
]
"nullable": [true]
},
"hash": "03e5b2bccf8bffb962a56443448311800bb832efe37fe6c52181bd7bf631740c"
}

View file

@ -37,14 +37,7 @@
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
false,
false,
false
]
"nullable": [false, false, false, false, false, false]
},
"hash": "37157927724c8bc647bf4f76f5698631cbd40637778dfa83e8f644ae6a7cf75b"
}

View file

@ -12,9 +12,7 @@
"parameters": {
"Right": 6
},
"nullable": [
false
]
"nullable": [false]
},
"hash": "3870c77c7c5fbb9bdd57c365765178a08de20e442a07f3e734e61c410e4f338e"
}

View file

@ -17,10 +17,7 @@
"parameters": {
"Right": 1
},
"nullable": [
true,
true
]
"nullable": [true, true]
},
"hash": "d32d91ca2debc4212841282533482b2ff081234c7f9f848a7223ae04234995d9"
}

View file

@ -22,11 +22,7 @@
"parameters": {
"Right": 2
},
"nullable": [
false,
true,
false
]
"nullable": [false, true, false]
},
"hash": "ffa1b76d20c86d6bea02bd03e5e7de159adbb7c7c0ef585ce4df9ec648bea7f8"
}

View file

@ -174,7 +174,13 @@ impl Database {
}
/// Update a node's network after it has been identified
pub async fn update_node_network(&self, scheme: &str, host: &str, port: i64, network: &str) -> Result<()> {
pub async fn update_node_network(
&self,
scheme: &str,
host: &str,
port: i64,
network: &str,
) -> Result<()> {
let now = chrono::Utc::now().to_rfc3339();
let result = sqlx::query!(
@ -193,9 +199,15 @@ impl Database {
.await?;
if result.rows_affected() > 0 {
debug!("Updated network for node {}://{}:{} to {}", scheme, host, port, network);
debug!(
"Updated network for node {}://{}:{} to {}",
scheme, host, port, network
);
} else {
warn!("Failed to update network for node {}://{}:{}: not found", scheme, host, port);
warn!(
"Failed to update network for node {}://{}:{}: not found",
scheme, host, port
);
}
Ok(())
@ -213,14 +225,22 @@ impl Database {
let now = chrono::Utc::now().to_rfc3339();
// First get the node_id
let node_row = sqlx::query!("SELECT id FROM monero_nodes WHERE scheme = ? AND host = ? AND port = ?", scheme, host, port)
.fetch_optional(&self.pool)
.await?;
let node_row = sqlx::query!(
"SELECT id FROM monero_nodes WHERE scheme = ? AND host = ? AND port = ?",
scheme,
host,
port
)
.fetch_optional(&self.pool)
.await?;
let node_id = match node_row {
Some(row) => row.id,
None => {
warn!("Cannot record health check for unknown node: {}://{}:{}", scheme, host, port);
warn!(
"Cannot record health check for unknown node: {}://{}:{}",
scheme, host, port
);
return Ok(());
}
};

View file

@ -116,7 +116,12 @@ impl NodeDiscovery {
}
/// Enhanced health check that detects network and validates node identity
pub async fn check_node_health(&self, scheme: &str, host: &str, port: i64) -> Result<HealthCheckOutcome> {
pub async fn check_node_health(
&self,
scheme: &str,
host: &str,
port: i64,
) -> Result<HealthCheckOutcome> {
let start_time = Instant::now();
let rpc_request = serde_json::json!({
@ -228,7 +233,10 @@ impl NodeDiscovery {
let mut corrected_count = 0;
for node in all_nodes {
match self.check_node_health(&node.scheme, &node.host, node.port).await {
match self
.check_node_health(&node.scheme, &node.host, node.port)
.await
{
Ok(outcome) => {
// Always record the health check
self.db
@ -252,11 +260,17 @@ impl NodeDiscovery {
if let Some(discovered_network) = outcome.discovered_network {
let discovered_network_str = network_to_string(&discovered_network);
if node.network != discovered_network_str {
let node_url = format!("{}://{}:{}", node.scheme, node.host, node.port);
let node_url =
format!("{}://{}:{}", node.scheme, node.host, node.port);
warn!("Network mismatch detected for node {}: stored={}, discovered={}. Correcting...",
node_url, node.network, discovered_network_str);
self.db
.update_node_network(&node.scheme, &node.host, node.port, &discovered_network_str)
.update_node_network(
&node.scheme,
&node.host,
node.port,
&discovered_network_str,
)
.await?;
corrected_count += 1;
}

View file

@ -71,7 +71,8 @@ impl NodePool {
debug!(
"Selected node using P2C for network {}: {}",
self.network, selected.full_url()
self.network,
selected.full_url()
);
Ok(Some(selected.full_url()))
@ -100,7 +101,13 @@ impl NodePool {
score
}
pub async fn record_success(&self, scheme: &str, host: &str, port: i64, latency_ms: f64) -> Result<()> {
pub async fn record_success(
&self,
scheme: &str,
host: &str,
port: i64,
latency_ms: f64,
) -> Result<()> {
self.db
.record_health_check(scheme, host, port, true, Some(latency_ms))
.await?;
@ -108,7 +115,9 @@ impl NodePool {
}
pub async fn record_failure(&self, scheme: &str, host: &str, port: i64) -> Result<()> {
self.db.record_health_check(scheme, host, port, false, None).await?;
self.db
.record_health_check(scheme, host, port, false, None)
.await?;
Ok(())
}

View file

@ -151,15 +151,24 @@ async fn raw_http_request(
async fn record_success(state: &AppState, scheme: &str, host: &str, port: i64, latency_ms: f64) {
let node_pool_guard = state.node_pool.read().await;
if let Err(e) = node_pool_guard.record_success(scheme, host, port, latency_ms).await {
error!("Failed to record success for {}://{}:{}: {}", scheme, host, port, e);
if let Err(e) = node_pool_guard
.record_success(scheme, host, port, latency_ms)
.await
{
error!(
"Failed to record success for {}://{}:{}: {}",
scheme, host, port, e
);
}
}
async fn record_failure(state: &AppState, scheme: &str, host: &str, port: i64) {
let node_pool_guard = state.node_pool.read().await;
if let Err(e) = node_pool_guard.record_failure(scheme, host, port).await {
error!("Failed to record failure for {}://{}:{}: {}", scheme, host, port, e);
error!(
"Failed to record failure for {}://{}:{}: {}",
scheme, host, port, e
);
}
}
@ -367,7 +376,7 @@ async fn race_requests(
Ok((response, winning_node, latency_ms)) => {
let (scheme, host, port) = &winning_node;
let winning_node = format!("{}://{}:{}", scheme, host, port);
match &jsonrpc_method {
Some(rpc_method) => {
debug!(

View file

@ -1,6 +1,6 @@
[package]
name = "unstoppableswap-gui-rs"
version = "2.3.0-beta.1"
version = "2.3.0-beta.2"
authors = [ "binarybaron", "einliterflasche", "unstoppableswap" ]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,6 +1,6 @@
{
"productName": "UnstoppableSwap",
"version": "2.3.0-beta.1",
"version": "2.3.0-beta.2",
"identifier": "net.unstoppableswap.gui",
"build": {
"devUrl": "http://localhost:1420",

View file

@ -1,6 +1,6 @@
[package]
name = "swap"
version = "2.3.0-beta.1"
version = "2.3.0-beta.2"
authors = ["The COMIT guys <hello@comit.network>"]
edition = "2021"
description = "XMR/BTC trustless atomic swaps."
@ -37,6 +37,7 @@ 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"] }
@ -46,7 +47,6 @@ monero = { version = "0.12", features = ["serde_support"] }
monero-rpc = { path = "../monero-rpc" }
monero-rpc-pool = { path = "../monero-rpc-pool" }
monero-sys = { path = "../monero-sys" }
electrum-pool = { path = "../electrum-pool" }
once_cell = "1.19"
pem = "3.0"
proptest = "1"

View file

@ -457,8 +457,7 @@ impl From<RpcErrorCode> for i64 {
pub fn parse_rpc_error_code(error: &anyhow::Error) -> anyhow::Result<i64> {
// First try to extract an Electrum error from a MultiError if present
if let Some(multi_error) = error.downcast_ref::<electrum_pool::MultiError>()
{
if let Some(multi_error) = error.downcast_ref::<electrum_pool::MultiError>() {
// Try to find the first Electrum error in the MultiError
for single_error in multi_error.iter() {
if let bdk_electrum::electrum_client::Error::Protocol(serde_json::Value::String(

View file

@ -41,8 +41,8 @@ use tracing::{debug_span, Instrument};
use super::bitcoin_address::revalidate_network;
use super::BlockHeight;
use electrum_pool::ElectrumBalancer;
use derive_builder::Builder;
use electrum_pool::ElectrumBalancer;
use moka;
/// We allow transaction fees of up to 20% of the transferred amount to ensure

View file

@ -67,16 +67,9 @@ pub fn init(
"libp2p_dcutr",
"monero_cpp",
];
let OUR_CRATES: Vec<&str> = vec![
"swap",
"asb",
"monero_sys",
"unstoppableswap-gui-rs",
];
let INFO_LEVEL_CRATES: Vec<&str> = vec![
"monero_rpc_pool",
];
let OUR_CRATES: Vec<&str> = vec!["swap", "asb", "monero_sys", "unstoppableswap-gui-rs"];
let INFO_LEVEL_CRATES: Vec<&str> = vec!["monero_rpc_pool"];
// General log file for non-verbose logs
let file_appender: RollingFileAppender = tracing_appender::rolling::never(&dir, "swap-all.log");
@ -101,7 +94,11 @@ pub fn init(
.with_file(true)
.with_line_number(true)
.json()
.with_filter(env_filter_with_info_crates(level_filter, OUR_CRATES.clone(), INFO_LEVEL_CRATES.clone())?);
.with_filter(env_filter_with_info_crates(
level_filter,
OUR_CRATES.clone(),
INFO_LEVEL_CRATES.clone(),
)?);
// Layer for writing to the verbose log file
// Crates: All crates with different levels (libp2p at INFO+, others at TRACE)
@ -163,7 +160,11 @@ pub fn init(
TOR_CRATES.clone(),
INFO_LEVEL_CRATES.clone(),
)?,
false => env_filter_with_info_crates(level_filter, OUR_CRATES.clone(), INFO_LEVEL_CRATES.clone())?,
false => env_filter_with_info_crates(
level_filter,
OUR_CRATES.clone(),
INFO_LEVEL_CRATES.clone(),
)?,
};
let final_terminal_layer = match format {
@ -190,7 +191,6 @@ pub fn init(
Ok(())
}
/// This function controls which crate's logs actually get logged and from which level, with info-level crates at INFO level or higher.
fn env_filter_with_info_crates(
level_filter: LevelFilter,