mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-04-20 16:06:00 -04:00
Merge branch 'master' into redact-logs
This commit is contained in:
commit
57e80fced1
2
.github/workflows/draft-new-release.yml
vendored
2
.github/workflows/draft-new-release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
run: git checkout -b release/${{ github.event.inputs.version }}
|
||||
|
||||
- name: Update changelog
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@3.0.0
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@3.1.0
|
||||
with:
|
||||
version: ${{ github.event.inputs.version }}
|
||||
changelogPath: CHANGELOG.md
|
||||
|
@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.13.4] - 2024-07-25
|
||||
|
||||
- ASB: The `history` command can now be used while the asb is running.
|
||||
- ASB: Retry locking of Monero if it fails on first attempt
|
||||
|
||||
## [0.13.3] - 2024-07-15
|
||||
|
||||
@ -372,7 +375,8 @@ It is possible to migrate critical data from the old db to the sqlite but there
|
||||
- Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them.
|
||||
Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version.
|
||||
|
||||
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.3...HEAD
|
||||
[unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.4...HEAD
|
||||
[0.13.4]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.3...0.13.4
|
||||
[0.13.3]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.2...0.13.3
|
||||
[0.13.2]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.1...0.13.2
|
||||
[0.13.1]: https://github.com/comit-network/xmr-btc-swap/compare/0.13.0...0.13.1
|
||||
|
128
Cargo.lock
generated
128
Cargo.lock
generated
@ -752,7 +752,7 @@ dependencies = [
|
||||
"nom",
|
||||
"pathdiff",
|
||||
"serde",
|
||||
"toml 0.8.15",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1783,6 +1783,7 @@ dependencies = [
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite 0.2.13",
|
||||
"smallvec",
|
||||
@ -2602,14 +2603,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mockito"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f6e023aa5bdf392aa06c78e4a4e6d498baab5138d0c993503350ebbc37bf1e"
|
||||
checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"bytes",
|
||||
"colored",
|
||||
"futures-core",
|
||||
"hyper 0.14.28",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"rand 0.8.3",
|
||||
"regex",
|
||||
@ -4016,9 +4022,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -4045,9 +4051,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4056,20 +4062,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.118"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -4536,7 +4543,7 @@ checksum = "8049cf85f0e715d6af38dde439cb0ccb91f67fb9f5f63c80f8b43e48356e1a3f"
|
||||
|
||||
[[package]]
|
||||
name = "swap"
|
||||
version = "0.13.3"
|
||||
version = "0.13.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@ -4602,7 +4609,7 @@ dependencies = [
|
||||
"tokio-tar",
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"toml 0.8.15",
|
||||
"toml 0.8.19",
|
||||
"torut",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@ -4657,14 +4664,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4864,9 +4872,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-socks"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
|
||||
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"futures-util",
|
||||
@ -4942,9 +4950,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.15"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@ -4954,18 +4962,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.6"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.16"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
@ -5640,7 +5648,16 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5660,17 +5677,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5681,9 +5699,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@ -5699,9 +5717,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@ -5717,9 +5735,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@ -5735,9 +5759,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@ -5753,9 +5777,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@ -5765,9 +5789,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@ -5783,15 +5807,15 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.5"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "swap"
|
||||
version = "0.13.3"
|
||||
version = "0.13.4"
|
||||
authors = [ "The COMIT guys <hello@comit.network>" ]
|
||||
edition = "2021"
|
||||
description = "XMR/BTC trustless atomic swaps."
|
||||
@ -83,7 +83,7 @@ bitcoin-harness = { git = "https://github.com/delta1/bitcoin-harness-rs.git", re
|
||||
get-port = "3"
|
||||
hyper = "1.4"
|
||||
jsonrpsee = { version = "0.16.2", features = [ "ws-client" ] }
|
||||
mockito = "1.4"
|
||||
mockito = "1.5"
|
||||
monero-harness = { path = "../monero-harness" }
|
||||
port_check = "0.2"
|
||||
proptest = "1"
|
||||
|
@ -170,6 +170,7 @@ pub struct Context {
|
||||
pub swap_lock: Arc<SwapLock>,
|
||||
pub config: Config,
|
||||
pub tasks: Arc<PendingTaskList>,
|
||||
pub is_daemon: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -183,6 +184,7 @@ impl Context {
|
||||
debug: bool,
|
||||
json: bool,
|
||||
server_address: Option<SocketAddr>,
|
||||
is_daemon: bool,
|
||||
) -> Result<Context> {
|
||||
let data_dir = data::data_dir_from(data, is_testnet)?;
|
||||
let env_config = env_config_from(is_testnet);
|
||||
@ -251,6 +253,7 @@ impl Context {
|
||||
},
|
||||
swap_lock: Arc::new(SwapLock::new()),
|
||||
tasks: Arc::new(PendingTaskList::default()),
|
||||
is_daemon,
|
||||
};
|
||||
|
||||
Ok(context)
|
||||
@ -275,6 +278,7 @@ impl Context {
|
||||
monero_rpc_process: None,
|
||||
swap_lock: Arc::new(SwapLock::new()),
|
||||
tasks: Arc::new(PendingTaskList::default()),
|
||||
is_daemon: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,12 @@ use crate::protocol::bob::{BobState, Swap};
|
||||
use crate::protocol::{bob, State};
|
||||
use crate::{bitcoin, cli, monero, rpc};
|
||||
use anyhow::{bail, Context as AnyContext, Result};
|
||||
use comfy_table::Table;
|
||||
use libp2p::core::Multiaddr;
|
||||
use qrcode::render::unicode;
|
||||
use qrcode::QrCode;
|
||||
use rust_decimal::prelude::FromPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use serde_json::json;
|
||||
use std::cmp::min;
|
||||
use std::convert::TryInto;
|
||||
@ -652,14 +655,97 @@ impl Request {
|
||||
})
|
||||
}
|
||||
Method::History => {
|
||||
let swaps = context.db.all().await?;
|
||||
let mut vec: Vec<(Uuid, String)> = Vec::new();
|
||||
for (swap_id, state) in swaps {
|
||||
let state: BobState = state.try_into()?;
|
||||
vec.push((swap_id, state.to_string()));
|
||||
let mut table = Table::new();
|
||||
table.set_header(vec![
|
||||
"Swap ID",
|
||||
"Start Date",
|
||||
"State",
|
||||
"BTC Amount",
|
||||
"XMR Amount",
|
||||
"Exchange Rate",
|
||||
"Trading Partner Peer ID",
|
||||
]);
|
||||
|
||||
let all_swaps = context.db.all().await?;
|
||||
let mut json_results = Vec::new();
|
||||
|
||||
for (swap_id, state) in all_swaps {
|
||||
let result: Result<_> = async {
|
||||
let latest_state: BobState = state.try_into()?;
|
||||
let all_states = context.db.get_states(swap_id).await?;
|
||||
let state3 = all_states
|
||||
.iter()
|
||||
.find_map(|s| {
|
||||
if let State::Bob(BobState::BtcLocked { state3, .. }) = s {
|
||||
Some(state3)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.context("Failed to get \"BtcLocked\" state")?;
|
||||
|
||||
let swap_start_date = context.db.get_swap_start_date(swap_id).await?;
|
||||
let peer_id = context.db.get_peer_id(swap_id).await?;
|
||||
let btc_amount = state3.tx_lock.lock_amount();
|
||||
let xmr_amount = state3.xmr;
|
||||
let exchange_rate = Decimal::from_f64(btc_amount.to_btc())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Failed to convert BTC amount to Decimal")
|
||||
})?
|
||||
.checked_div(xmr_amount.as_xmr())
|
||||
.ok_or_else(|| anyhow::anyhow!("Division by zero or overflow"))?;
|
||||
let exchange_rate = format!("{} XMR/BTC", exchange_rate.round_dp(8));
|
||||
|
||||
let swap_data = json!({
|
||||
"swapId": swap_id.to_string(),
|
||||
"startDate": swap_start_date.to_string(),
|
||||
"state": latest_state.to_string(),
|
||||
"btcAmount": btc_amount.to_string(),
|
||||
"xmrAmount": xmr_amount.to_string(),
|
||||
"exchangeRate": exchange_rate,
|
||||
"tradingPartnerPeerId": peer_id.to_string()
|
||||
});
|
||||
|
||||
if context.config.json {
|
||||
tracing::info!(
|
||||
swap_id = %swap_id,
|
||||
swap_start_date = %swap_start_date,
|
||||
latest_state = %latest_state,
|
||||
btc_amount = %btc_amount,
|
||||
xmr_amount = %xmr_amount,
|
||||
exchange_rate = %exchange_rate,
|
||||
trading_partner_peer_id = %peer_id,
|
||||
"Found swap in database"
|
||||
);
|
||||
} else {
|
||||
table.add_row(vec![
|
||||
swap_id.to_string(),
|
||||
swap_start_date.to_string(),
|
||||
latest_state.to_string(),
|
||||
btc_amount.to_string(),
|
||||
xmr_amount.to_string(),
|
||||
exchange_rate,
|
||||
peer_id.to_string(),
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(swap_data)
|
||||
}
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(swap_data) => json_results.push(swap_data),
|
||||
Err(e) => {
|
||||
tracing::error!(swap_id = %swap_id, error = %e, "Failed to get swap details")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(json!({ "swaps": vec }))
|
||||
if !context.config.json && !context.is_daemon {
|
||||
println!("{}", table);
|
||||
}
|
||||
|
||||
Ok(json!({"swaps": json_results}))
|
||||
}
|
||||
Method::Logs { logs_dir, redact, swap_id } => {
|
||||
let dir = logs_dir.unwrap_or(context.config.data_dir.join("logs"));
|
||||
|
@ -31,12 +31,12 @@ where
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Start { resume_only },
|
||||
},
|
||||
RawCommand::History => Arguments {
|
||||
RawCommand::History { only_unfinished } => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::History,
|
||||
cmd: Command::History { only_unfinished },
|
||||
},
|
||||
RawCommand::Logs {
|
||||
logs_dir: dir_path,
|
||||
@ -197,7 +197,9 @@ pub enum Command {
|
||||
Start {
|
||||
resume_only: bool,
|
||||
},
|
||||
History,
|
||||
History {
|
||||
only_unfinished: bool,
|
||||
},
|
||||
Config,
|
||||
Logs {
|
||||
logs_dir: Option<PathBuf>,
|
||||
@ -275,8 +277,6 @@ pub enum RawCommand {
|
||||
)]
|
||||
resume_only: bool,
|
||||
},
|
||||
#[structopt(about = "Prints swap-id and the state of each swap ever made.")]
|
||||
History,
|
||||
#[structopt(about = "Prints all logging messages issued in the past.")]
|
||||
Logs {
|
||||
#[structopt(
|
||||
@ -296,6 +296,14 @@ pub enum RawCommand {
|
||||
)]
|
||||
swap_id: Option<Uuid>
|
||||
},
|
||||
#[structopt(about = "Prints swap-id and the state of each swap ever made.")]
|
||||
History {
|
||||
#[structopt(
|
||||
long = "only-unfinished",
|
||||
help = "If set, only unfinished swaps will be printed."
|
||||
)]
|
||||
only_unfinished: bool,
|
||||
},
|
||||
#[structopt(about = "Prints the current config")]
|
||||
Config,
|
||||
#[structopt(about = "Allows withdrawing BTC from the internal Bitcoin wallet.")]
|
||||
@ -411,7 +419,9 @@ mod tests {
|
||||
json: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::History,
|
||||
cmd: Command::History {
|
||||
only_unfinished: false,
|
||||
},
|
||||
};
|
||||
let args = parse_args(raw_ars).unwrap();
|
||||
assert_eq!(expected_args, args);
|
||||
@ -586,7 +596,9 @@ mod tests {
|
||||
json: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::History,
|
||||
cmd: Command::History {
|
||||
only_unfinished: false,
|
||||
},
|
||||
};
|
||||
let args = parse_args(raw_ars).unwrap();
|
||||
assert_eq!(expected_args, args);
|
||||
|
@ -19,6 +19,8 @@ use libp2p::core::Multiaddr;
|
||||
use libp2p::swarm::AddressScore;
|
||||
use libp2p::Swarm;
|
||||
use swap::common::tracing_util::Format;
|
||||
use rust_decimal::prelude::FromPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryInto;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
@ -34,7 +36,9 @@ use swap::common::{self, check_latest_version, get_logs};
|
||||
use swap::database::{open_db, AccessMode};
|
||||
use swap::network::rendezvous::XmrBtcNamespace;
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::alice::swap::is_complete;
|
||||
use swap::protocol::alice::{run, AliceState};
|
||||
use swap::protocol::State;
|
||||
use swap::seed::Seed;
|
||||
use swap::tor::AuthenticatedClient;
|
||||
use swap::{bitcoin, kraken, monero, tor};
|
||||
@ -233,19 +237,87 @@ async fn main() -> Result<()> {
|
||||
|
||||
event_loop.run().await;
|
||||
}
|
||||
Command::History => {
|
||||
Command::History { only_unfinished } => {
|
||||
let db = open_db(config.data.dir.join("sqlite"), AccessMode::ReadOnly).await?;
|
||||
let mut table: Table = Table::new();
|
||||
|
||||
let mut table = Table::new();
|
||||
table.set_header(vec![
|
||||
"Swap ID",
|
||||
"Start Date",
|
||||
"State",
|
||||
"BTC Amount",
|
||||
"XMR Amount",
|
||||
"Exchange Rate",
|
||||
"Trading Partner Peer ID",
|
||||
"Completed",
|
||||
]);
|
||||
|
||||
table.set_header(vec!["SWAP ID", "STATE"]);
|
||||
let all_swaps = db.all().await?;
|
||||
for (swap_id, state) in all_swaps {
|
||||
if let Err(e) = async {
|
||||
let latest_state: AliceState = state.try_into()?;
|
||||
let is_completed = is_complete(&latest_state);
|
||||
|
||||
for (swap_id, state) in db.all().await? {
|
||||
let state: AliceState = state.try_into()?;
|
||||
table.add_row(vec![swap_id.to_string(), state.to_string()]);
|
||||
if only_unfinished && is_completed {
|
||||
return Ok::<_, anyhow::Error>(());
|
||||
}
|
||||
|
||||
let all_states = db.get_states(swap_id).await?;
|
||||
let state3 = all_states
|
||||
.iter()
|
||||
.find_map(|s| match s {
|
||||
State::Alice(AliceState::BtcLockTransactionSeen { state3 }) => {
|
||||
Some(state3)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.context("Failed to get \"BtcLockTransactionSeen\" state")?;
|
||||
|
||||
let swap_start_date = db.get_swap_start_date(swap_id).await?;
|
||||
let peer_id = db.get_peer_id(swap_id).await?;
|
||||
|
||||
let exchange_rate = Decimal::from_f64(state3.btc.to_btc())
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to convert BTC amount to Decimal"))?
|
||||
.checked_div(state3.xmr.as_xmr())
|
||||
.ok_or_else(|| anyhow::anyhow!("Division by zero or overflow"))?;
|
||||
let exchange_rate = format!("{} XMR/BTC", exchange_rate.round_dp(8));
|
||||
|
||||
if json {
|
||||
tracing::info!(
|
||||
swap_id = %swap_id,
|
||||
swap_start_date = %swap_start_date,
|
||||
latest_state = %latest_state,
|
||||
btc_amount = %state3.btc,
|
||||
xmr_amount = %state3.xmr,
|
||||
exchange_rate = %exchange_rate,
|
||||
trading_partner_peer_id = %peer_id,
|
||||
completed = is_completed,
|
||||
"Found swap in database"
|
||||
);
|
||||
} else {
|
||||
table.add_row(vec![
|
||||
swap_id.to_string(),
|
||||
swap_start_date.to_string(),
|
||||
latest_state.to_string(),
|
||||
state3.btc.to_string(),
|
||||
state3.xmr.to_string(),
|
||||
exchange_rate,
|
||||
peer_id.to_string(),
|
||||
is_completed.to_string(),
|
||||
]);
|
||||
}
|
||||
|
||||
Ok::<_, anyhow::Error>(())
|
||||
}
|
||||
.await
|
||||
{
|
||||
tracing::error!(swap_id = %swap_id, error = %e, "Failed to get swap details");
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", table);
|
||||
if !json {
|
||||
println!("{}", table);
|
||||
}
|
||||
}
|
||||
Command::Config => {
|
||||
let config_json = serde_json::to_string_pretty(&config)?;
|
||||
|
@ -78,6 +78,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -100,7 +101,8 @@ where
|
||||
let request = Request::new(Method::History);
|
||||
|
||||
let context =
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None, false)
|
||||
.await?;
|
||||
(context, request)
|
||||
}
|
||||
CliCommand::Logs {
|
||||
@ -117,7 +119,8 @@ where
|
||||
let request = Request::new(Method::Config);
|
||||
|
||||
let context =
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None, false)
|
||||
.await?;
|
||||
(context, request)
|
||||
}
|
||||
CliCommand::Balance { bitcoin } => {
|
||||
@ -134,6 +137,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -155,6 +159,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
server_address,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -176,6 +181,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -197,6 +203,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -217,6 +224,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -227,8 +235,18 @@ where
|
||||
} => {
|
||||
let request = Request::new(Method::ListSellers { rendezvous_point });
|
||||
|
||||
let context =
|
||||
Context::build(None, None, Some(tor), data, is_testnet, debug, json, None).await?;
|
||||
let context = Context::build(
|
||||
None,
|
||||
None,
|
||||
Some(tor),
|
||||
data,
|
||||
is_testnet,
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
(context, request)
|
||||
}
|
||||
@ -244,6 +262,7 @@ where
|
||||
debug,
|
||||
json,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
(context, request)
|
||||
@ -254,7 +273,8 @@ where
|
||||
let request = Request::new(Method::MoneroRecovery { swap_id });
|
||||
|
||||
let context =
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?;
|
||||
Context::build(None, None, None, data, is_testnet, debug, json, None, false)
|
||||
.await?;
|
||||
|
||||
(context, request)
|
||||
}
|
||||
|
@ -142,6 +142,14 @@ impl Amount {
|
||||
Decimal::from(self.as_piconero())
|
||||
}
|
||||
|
||||
pub fn as_xmr(&self) -> Decimal {
|
||||
let mut decimal = Decimal::from(self.0);
|
||||
decimal
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
decimal
|
||||
}
|
||||
|
||||
fn from_decimal(amount: Decimal) -> Result<Self> {
|
||||
let piconeros_dec =
|
||||
amount.mul(Decimal::from_u64(PICONERO_OFFSET).expect("constant to fit into u64"));
|
||||
@ -184,11 +192,8 @@ impl From<Amount> for u64 {
|
||||
|
||||
impl fmt::Display for Amount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut decimal = Decimal::from(self.0);
|
||||
decimal
|
||||
.set_scale(12)
|
||||
.expect("12 is smaller than max precision of 28");
|
||||
write!(f, "{} XMR", decimal)
|
||||
let xmr_value = self.as_xmr();
|
||||
write!(f, "{} XMR", xmr_value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,8 +384,8 @@ pub struct State3 {
|
||||
S_b_bitcoin: bitcoin::PublicKey,
|
||||
pub v: monero::PrivateViewKey,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
pub btc: bitcoin::Amount,
|
||||
pub xmr: monero::Amount,
|
||||
pub cancel_timelock: CancelTimelock,
|
||||
pub punish_timelock: PunishTimelock,
|
||||
refund_address: bitcoin::Address,
|
||||
|
@ -1,11 +1,14 @@
|
||||
//! Run an XMR/BTC swap in the role of Alice.
|
||||
//! Alice holds XMR and wishes receive BTC.
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::asb::{EventLoopHandle, LatestRate};
|
||||
use crate::bitcoin::ExpiredTimelocks;
|
||||
use crate::env::Config;
|
||||
use crate::protocol::alice::{AliceState, Swap};
|
||||
use crate::{bitcoin, monero};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use backoff::ExponentialBackoffBuilder;
|
||||
use tokio::select;
|
||||
use tokio::time::timeout;
|
||||
use uuid::Uuid;
|
||||
@ -111,23 +114,63 @@ where
|
||||
}
|
||||
}
|
||||
AliceState::BtcLocked { state3 } => {
|
||||
match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||
ExpiredTimelocks::None { .. } => {
|
||||
// Record the current monero wallet block height so we don't have to scan from
|
||||
// block 0 for scenarios where we create a refund wallet.
|
||||
let monero_wallet_restore_blockheight = monero_wallet.block_height().await?;
|
||||
// We retry to lock the Monero wallet until we succeed or until the cancel timelock expires.
|
||||
//
|
||||
// This is necessary because the monero-wallet-rpc can sometimes error out due to various reasons, such as
|
||||
// - no connection to the daemon
|
||||
// - "failed to get output distribution"
|
||||
// See https://github.com/comit-network/xmr-btc-swap/issues/1726
|
||||
let backoff = ExponentialBackoffBuilder::new()
|
||||
.with_initial_interval(Duration::from_secs(5))
|
||||
.with_max_interval(Duration::from_secs(60 * 3))
|
||||
.with_max_elapsed_time(None)
|
||||
.build();
|
||||
|
||||
let transfer_proof = monero_wallet
|
||||
.transfer(state3.lock_xmr_transfer_request())
|
||||
.await?;
|
||||
let result = backoff::future::retry_notify(
|
||||
backoff,
|
||||
|| async {
|
||||
match state3.expired_timelocks(bitcoin_wallet).await {
|
||||
Ok(ExpiredTimelocks::None { .. }) => {
|
||||
// Record the current monero wallet block height so we don't have to scan from
|
||||
// block 0 for scenarios where we create a refund wallet.
|
||||
let monero_wallet_restore_blockheight = monero_wallet
|
||||
.block_height()
|
||||
.await
|
||||
.map_err(backoff::Error::transient)?;
|
||||
|
||||
let transfer_proof = monero_wallet
|
||||
.transfer(state3.lock_xmr_transfer_request())
|
||||
.await
|
||||
.map_err(backoff::Error::transient)?;
|
||||
|
||||
Ok(Some((monero_wallet_restore_blockheight, transfer_proof)))
|
||||
}
|
||||
Ok(_) => Ok(None),
|
||||
Err(e) => Err(backoff::Error::transient(e)),
|
||||
}
|
||||
},
|
||||
|err, delay: Duration| {
|
||||
tracing::warn!(
|
||||
%err,
|
||||
delay_secs = delay.as_secs(),
|
||||
"Failed to lock XMR. We will retry after a delay"
|
||||
);
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(Some((monero_wallet_restore_blockheight, transfer_proof))) => {
|
||||
AliceState::XmrLockTransactionSent {
|
||||
monero_wallet_restore_blockheight,
|
||||
transfer_proof,
|
||||
state3,
|
||||
}
|
||||
}
|
||||
_ => AliceState::SafelyAborted,
|
||||
Ok(None) => AliceState::SafelyAborted,
|
||||
Err(e) => {
|
||||
unreachable!("We should retry forever until the cancel timelock expires. But we got an error: {:#}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
AliceState::XmrLockTransactionSent {
|
||||
@ -397,7 +440,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn is_complete(state: &AliceState) -> bool {
|
||||
pub fn is_complete(state: &AliceState) -> bool {
|
||||
matches!(
|
||||
state,
|
||||
AliceState::XmrRefunded
|
||||
|
@ -369,7 +369,7 @@ pub struct State3 {
|
||||
S_a_monero: monero::PublicKey,
|
||||
S_a_bitcoin: bitcoin::PublicKey,
|
||||
v: monero::PrivateViewKey,
|
||||
xmr: monero::Amount,
|
||||
pub xmr: monero::Amount,
|
||||
pub cancel_timelock: CancelTimelock,
|
||||
punish_timelock: PunishTimelock,
|
||||
refund_address: bitcoin::Address,
|
||||
|
@ -103,13 +103,26 @@ mod test {
|
||||
|
||||
let (client, _, _) = setup_daemon(harness_ctx).await;
|
||||
|
||||
let response: HashMap<String, Vec<(Uuid, String)>> = client
|
||||
let response: HashMap<String, Vec<Value>> = client
|
||||
.request("get_history", ObjectParams::new())
|
||||
.await
|
||||
.unwrap();
|
||||
let swaps: Vec<(Uuid, String)> = vec![(bob_swap_id, "btc is locked".to_string())];
|
||||
|
||||
assert_eq!(response, HashMap::from([("swaps".to_string(), swaps)]));
|
||||
let swaps = response.get("swaps").unwrap();
|
||||
assert_eq!(swaps.len(), 1);
|
||||
|
||||
assert_has_keys_serde(
|
||||
swaps[0].as_object().unwrap(),
|
||||
&[
|
||||
"swapId",
|
||||
"startDate",
|
||||
"state",
|
||||
"btcAmount",
|
||||
"xmrAmount",
|
||||
"exchangeRate",
|
||||
"tradingPartnerPeerId",
|
||||
],
|
||||
);
|
||||
|
||||
let response: HashMap<String, HashMap<Uuid, Vec<Value>>> = client
|
||||
.request("get_raw_states", ObjectParams::new())
|
||||
|
Loading…
x
Reference in New Issue
Block a user