mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-09-13 01:21:49 -04:00
bump: release version 2.3.0-beta.2
This commit is contained in:
parent
dc865a91f3
commit
a235a537c1
21 changed files with 102 additions and 71 deletions
|
@ -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
4
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
"nullable": [true]
|
||||
},
|
||||
"hash": "03e5b2bccf8bffb962a56443448311800bb832efe37fe6c52181bd7bf631740c"
|
||||
}
|
||||
|
|
|
@ -37,14 +37,7 @@
|
|||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
"nullable": [false, false, false, false, false, false]
|
||||
},
|
||||
"hash": "37157927724c8bc647bf4f76f5698631cbd40637778dfa83e8f644ae6a7cf75b"
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
"nullable": [false]
|
||||
},
|
||||
"hash": "3870c77c7c5fbb9bdd57c365765178a08de20e442a07f3e734e61c410e4f338e"
|
||||
}
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true
|
||||
]
|
||||
"nullable": [true, true]
|
||||
},
|
||||
"hash": "d32d91ca2debc4212841282533482b2ff081234c7f9f848a7223ae04234995d9"
|
||||
}
|
||||
|
|
|
@ -22,11 +22,7 @@
|
|||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
"nullable": [false, true, false]
|
||||
},
|
||||
"hash": "ffa1b76d20c86d6bea02bd03e5e7de159adbb7c7c0ef585ce4df9ec648bea7f8"
|
||||
}
|
||||
|
|
|
@ -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(());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue