mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-24 22:19:37 -04:00
bump version: 2.3.0-beta
This commit is contained in:
parent
130e93bf9b
commit
2cc1d0e7e5
14 changed files with 71 additions and 66 deletions
|
@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- **Load Balancing & Reliability**: Introduced `monero-rpc-pool`, a load-balancing proxy for Monero RPC nodes that automatically discovers healthy nodes and routes requests to improve connection reliability.
|
## [2.3.0-beta] - 2025-06-19
|
||||||
- **ASB Configuration**: Added `monero_node_pool` boolean option to ASB config. When enabled, the ASB uses the internal Monero RPC pool instead of connecting directly to a single daemon URL, providing improved reliability and automatic failover across multiple Monero nodes.
|
|
||||||
|
- ASB + CLI + GUI: Introduce a load-balancing proxy for Monero RPC nodes that automatically discovers healthy nodes and routes requests to improve connection reliability.
|
||||||
|
- ASB: Added `monero_node_pool` boolean option to ASB config. When enabled, the ASB uses the internal Monero RPC pool instead of connecting directly to a single daemon URL, providing improved reliability and automatic failover across multiple Monero nodes.
|
||||||
|
|
||||||
## [2.2.0-beta.2] - 2025-06-17
|
## [2.2.0-beta.2] - 2025-06-17
|
||||||
|
|
||||||
|
|
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -5932,8 +5932,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 4.5.38",
|
"clap 4.5.38",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"futures",
|
|
||||||
"monero",
|
|
||||||
"monero-rpc",
|
"monero-rpc",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -5947,7 +5945,6 @@ dependencies = [
|
||||||
"tower-http 0.5.2",
|
"tower-http 0.5.2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"typeshare",
|
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
@ -9596,7 +9593,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swap"
|
name = "swap"
|
||||||
version = "2.2.0-beta.2"
|
version = ""
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arti-client",
|
"arti-client",
|
||||||
|
@ -9634,7 +9631,6 @@ dependencies = [
|
||||||
"monero",
|
"monero",
|
||||||
"monero-harness",
|
"monero-harness",
|
||||||
"monero-rpc",
|
"monero-rpc",
|
||||||
"monero-rpc-pool",
|
|
||||||
"monero-sys",
|
"monero-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pem",
|
"pem",
|
||||||
|
@ -12141,10 +12137,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unstoppableswap-gui-rs"
|
name = "unstoppableswap-gui-rs"
|
||||||
version = "2.2.0-beta.2"
|
version = ""
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"monero-rpc-pool",
|
|
||||||
"rustls 0.23.27",
|
"rustls 0.23.27",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -17,10 +17,7 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [true, true]
|
||||||
true,
|
|
||||||
true
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"hash": "132666c849bf0db14e50ef41f429e17b7c1afd21031edf3af40fadfb79ef2597"
|
"hash": "132666c849bf0db14e50ef41f429e17b7c1afd21031edf3af40fadfb79ef2597"
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,15 +42,7 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 0
|
"Right": 0
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [false, false, false, false, false, false, false]
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"hash": "3e8f39a6ec4443cec6497672891d12bbf7c1d0aca061827740af88ced863ae23"
|
"hash": "3e8f39a6ec4443cec6497672891d12bbf7c1d0aca061827740af88ced863ae23"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,7 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 7
|
"Right": 7
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [false]
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"hash": "5798d9589772742f074e0ecc2551a40d943bfb7ed2e295f09f12d77cb65ce821"
|
"hash": "5798d9589772742f074e0ecc2551a40d943bfb7ed2e295f09f12d77cb65ce821"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,7 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [true]
|
||||||
true
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"hash": "e0865335c2dcb040a34e3f1305fe1a823d6fcde4a061def602cba30971817781"
|
"hash": "e0865335c2dcb040a34e3f1305fe1a823d6fcde4a061def602cba30971817781"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,7 @@
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 2
|
"Right": 2
|
||||||
},
|
},
|
||||||
"nullable": [
|
"nullable": [false, true, false]
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"hash": "ffa1b76d20c86d6bea02bd03e5e7de159adbb7c7c0ef585ce4df9ec648bea7f8"
|
"hash": "ffa1b76d20c86d6bea02bd03e5e7de159adbb7c7c0ef585ce4df9ec648bea7f8"
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,12 +332,12 @@ impl NodeDiscovery {
|
||||||
for node_url in nodes.iter() {
|
for node_url in nodes.iter() {
|
||||||
if let Ok(url) = url::Url::parse(node_url) {
|
if let Ok(url) = url::Url::parse(node_url) {
|
||||||
let scheme = url.scheme();
|
let scheme = url.scheme();
|
||||||
|
|
||||||
// Validate scheme - must be http or https
|
// Validate scheme - must be http or https
|
||||||
if !matches!(scheme, "http" | "https") {
|
if !matches!(scheme, "http" | "https") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate host - must be non-empty
|
// Validate host - must be non-empty
|
||||||
let Some(host) = url.host_str() else {
|
let Some(host) = url.host_str() else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -345,7 +345,7 @@ impl NodeDiscovery {
|
||||||
if host.is_empty() {
|
if host.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate port - must be present
|
// Validate port - must be present
|
||||||
let Some(port) = url.port() else {
|
let Some(port) = url.port() else {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -352,34 +352,48 @@ function MoneroRpcPoolSetting() {
|
||||||
function MoneroNodeUrlSetting() {
|
function MoneroNodeUrlSetting() {
|
||||||
const network = getNetwork();
|
const network = getNetwork();
|
||||||
const useMoneroRpcPool = useSettings((s) => s.useMoneroRpcPool);
|
const useMoneroRpcPool = useSettings((s) => s.useMoneroRpcPool);
|
||||||
const moneroNodeUrl = useSettings((s) => s.nodes[network][Blockchain.Monero][0] || "");
|
const moneroNodeUrl = useSettings(
|
||||||
|
(s) => s.nodes[network][Blockchain.Monero][0] || "",
|
||||||
|
);
|
||||||
const nodeStatuses = useNodes((s) => s.nodes);
|
const nodeStatuses = useNodes((s) => s.nodes);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
|
|
||||||
const currentNodes = useSettings((s) => s.nodes[network][Blockchain.Monero]);
|
const currentNodes = useSettings((s) => s.nodes[network][Blockchain.Monero]);
|
||||||
|
|
||||||
const handleNodeUrlChange = (newUrl: string) => {
|
const handleNodeUrlChange = (newUrl: string) => {
|
||||||
// Remove existing nodes and add the new one
|
// Remove existing nodes and add the new one
|
||||||
currentNodes.forEach(node => {
|
currentNodes.forEach((node) => {
|
||||||
dispatch(removeNode({ network, type: Blockchain.Monero, node }));
|
dispatch(removeNode({ network, type: Blockchain.Monero, node }));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newUrl.trim()) {
|
if (newUrl.trim()) {
|
||||||
dispatch(addNode({ network, type: Blockchain.Monero, node: newUrl.trim() }));
|
dispatch(
|
||||||
|
addNode({ network, type: Blockchain.Monero, node: newUrl.trim() }),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefreshStatus = async () => {
|
const handleRefreshStatus = async () => {
|
||||||
// Don't refresh if pool is enabled or no node URL is configured
|
// Don't refresh if pool is enabled or no node URL is configured
|
||||||
if (!moneroNodeUrl || useMoneroRpcPool) return;
|
if (!moneroNodeUrl || useMoneroRpcPool) return;
|
||||||
|
|
||||||
setIsRefreshing(true);
|
setIsRefreshing(true);
|
||||||
try {
|
try {
|
||||||
const status = await getNodeStatus(moneroNodeUrl, Blockchain.Monero, network);
|
const status = await getNodeStatus(
|
||||||
|
moneroNodeUrl,
|
||||||
|
Blockchain.Monero,
|
||||||
|
network,
|
||||||
|
);
|
||||||
|
|
||||||
// Update the status in the store
|
// Update the status in the store
|
||||||
dispatch(setStatus({ node: moneroNodeUrl, status, blockchain: Blockchain.Monero }));
|
dispatch(
|
||||||
|
setStatus({
|
||||||
|
node: moneroNodeUrl,
|
||||||
|
status,
|
||||||
|
blockchain: Blockchain.Monero,
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to refresh node status:", error);
|
console.error("Failed to refresh node status:", error);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -388,7 +402,9 @@ function MoneroNodeUrlSetting() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValid = (url: string) => url === "" || isValidUrl(url, ["http"]);
|
const isValid = (url: string) => url === "" || isValidUrl(url, ["http"]);
|
||||||
const nodeStatus = moneroNodeUrl ? nodeStatuses[Blockchain.Monero][moneroNodeUrl] : null;
|
const nodeStatus = moneroNodeUrl
|
||||||
|
? nodeStatuses[Blockchain.Monero][moneroNodeUrl]
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -416,26 +432,36 @@ function MoneroNodeUrlSetting() {
|
||||||
noErrorWhenEmpty
|
noErrorWhenEmpty
|
||||||
/>
|
/>
|
||||||
<>
|
<>
|
||||||
<Tooltip title={
|
<Tooltip
|
||||||
useMoneroRpcPool
|
title={
|
||||||
? "Node status checking is disabled when using the pool"
|
useMoneroRpcPool
|
||||||
: !moneroNodeUrl
|
? "Node status checking is disabled when using the pool"
|
||||||
? "Enter a node URL to check status"
|
: !moneroNodeUrl
|
||||||
: "Node status"
|
? "Enter a node URL to check status"
|
||||||
}>
|
: "Node status"
|
||||||
|
}
|
||||||
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
<Circle
|
<Circle
|
||||||
color={useMoneroRpcPool || !moneroNodeUrl ? "gray" : (nodeStatus ? "green" : "red")}
|
color={
|
||||||
|
useMoneroRpcPool || !moneroNodeUrl
|
||||||
|
? "gray"
|
||||||
|
: nodeStatus
|
||||||
|
? "green"
|
||||||
|
: "red"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={
|
<Tooltip
|
||||||
useMoneroRpcPool
|
title={
|
||||||
? "Node status refresh is disabled when using the pool"
|
useMoneroRpcPool
|
||||||
: !moneroNodeUrl
|
? "Node status refresh is disabled when using the pool"
|
||||||
? "Enter a node URL to refresh status"
|
: !moneroNodeUrl
|
||||||
: "Refresh node status"
|
? "Enter a node URL to refresh status"
|
||||||
}>
|
: "Refresh node status"
|
||||||
|
}
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleRefreshStatus}
|
onClick={handleRefreshStatus}
|
||||||
disabled={isRefreshing || useMoneroRpcPool || !moneroNodeUrl}
|
disabled={isRefreshing || useMoneroRpcPool || !moneroNodeUrl}
|
||||||
|
|
|
@ -225,8 +225,9 @@ export async function initializeContext() {
|
||||||
|
|
||||||
// For Monero nodes, get the configured node URL and pool setting
|
// For Monero nodes, get the configured node URL and pool setting
|
||||||
const useMoneroRpcPool = store.getState().settings.useMoneroRpcPool;
|
const useMoneroRpcPool = store.getState().settings.useMoneroRpcPool;
|
||||||
const moneroNodes = store.getState().settings.nodes[network][Blockchain.Monero];
|
const moneroNodes =
|
||||||
|
store.getState().settings.nodes[network][Blockchain.Monero];
|
||||||
|
|
||||||
// Always pass the first configured monero node URL directly without checking availability
|
// Always pass the first configured monero node URL directly without checking availability
|
||||||
// The backend will handle whether to use the pool or the custom node
|
// The backend will handle whether to use the pool or the custom node
|
||||||
const moneroNode = moneroNodes.length > 0 ? moneroNodes[0] : null;
|
const moneroNode = moneroNodes.length > 0 ? moneroNodes[0] : null;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "unstoppableswap-gui-rs"
|
name = "unstoppableswap-gui-rs"
|
||||||
version = "2.2.0-beta.2"
|
version = "2.3.0-beta"
|
||||||
authors = [ "binarybaron", "einliterflasche", "unstoppableswap" ]
|
authors = [ "binarybaron", "einliterflasche", "unstoppableswap" ]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"productName": "UnstoppableSwap",
|
"productName": "UnstoppableSwap",
|
||||||
"version": "2.2.0-beta.2",
|
"version": "2.3.0-beta",
|
||||||
"identifier": "net.unstoppableswap.gui",
|
"identifier": "net.unstoppableswap.gui",
|
||||||
"build": {
|
"build": {
|
||||||
"devUrl": "http://localhost:1420",
|
"devUrl": "http://localhost:1420",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "swap"
|
name = "swap"
|
||||||
version = "2.2.0-beta.2"
|
version = "2.3.0-beta"
|
||||||
authors = ["The COMIT guys <hello@comit.network>"]
|
authors = ["The COMIT guys <hello@comit.network>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "XMR/BTC trustless atomic swaps."
|
description = "XMR/BTC trustless atomic swaps."
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue