mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-24 06:59:36 -05:00
383: Improve resilience of balance assertions r=thomaseizinger a=thomaseizinger The final commit is the relevant patch! It sits on top of several refactoring commits that happened while I was debugging why things didn't work as expected. Turned out to be reasonably useful so I just left them in :) 385: Bump anyhow from 1.0.39 to 1.0.40 r=thomaseizinger a=dependabot[bot] Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.39 to 1.0.40. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/dtolnay/anyhow/releases">anyhow's releases</a>.</em></p> <blockquote> <h2>1.0.40</h2> <ul> <li>Reduce memory footprint of errors on Rust versions 1.51+ (<a href="https://github-redirect.dependabot.com/dtolnay/anyhow/issues/145">#145</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="704622f25d
"><code>704622f</code></a> Release 1.0.40</li> <li><a href="64ac0c00a9
"><code>64ac0c0</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/dtolnay/anyhow/issues/145">#145</a> from dtolnay/addrof</li> <li><a href="ef082670ea
"><code>ef08267</code></a> Eliminate functionally duplicate vtable methods on rustc 1.51+</li> <li><a href="1295b1fef9
"><code>1295b1f</code></a> Add additional builds on 1.50 and 1.51 validating addr_of codepath</li> <li><a href="be89adf403
"><code>be89adf</code></a> Detect whether ptr::addr_of is supported by current compiler</li> <li><a href="ac64560c42
"><code>ac64560</code></a> Switch object_ref return from real ref to Ref ptr</li> <li><a href="2987c9b59e
"><code>2987c9b</code></a> Ignore redundant_else pedantic clippy lint</li> <li><a href="827bb9d4c6
"><code>827bb9d</code></a> Catch some warnings in addr_of-related codepaths</li> <li><a href="ce0041866d
"><code>ce00418</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/dtolnay/anyhow/issues/144">#144</a> from dtolnay/ptr</li> <li><a href="3c32aa7dcd
"><code>3c32aa7</code></a> Relax Sized bound on Own, Ref, Mut ptrs</li> <li>Additional commits viewable in <a href="https://github.com/dtolnay/anyhow/compare/1.0.39...1.0.40">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=anyhow&package-manager=cargo&previous-version=1.0.39&new-version=1.0.40)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> 386: Bump hyper from 0.14.4 to 0.14.5 r=thomaseizinger a=dependabot[bot] Bumps [hyper](https://github.com/hyperium/hyper) from 0.14.4 to 0.14.5. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/hyperium/hyper/releases">hyper's releases</a>.</em></p> <blockquote> <h2>v0.14.5</h2> <h2>Bug Fixes</h2> <ul> <li><strong>client:</strong> omit default port from automatic Host headers (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2441">#2441</a>) (<a href="0b11eee9bd
">0b11eee9</a>)</li> <li><strong>headers:</strong> Support multiple Content-Length values on same line (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2471">#2471</a>) (<a href="48fdaf1606
">48fdaf16</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2470">#2470</a>)</li> <li><strong>server:</strong> skip automatic Content-Length headers when not allowed (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2216">#2216</a>) (<a href="8cbf9527df
">8cbf9527</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2215">#2215</a>)</li> </ul> <h2>Features</h2> <ul> <li><strong>client:</strong> allow HTTP/0.9 responses behind a flag (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2473">#2473</a>) (<a href="68d4e4a3db
">68d4e4a3</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2468">#2468</a>)</li> <li><strong>server:</strong> add <code>AddrIncoming::from_listener</code> constructor (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2439">#2439</a>) (<a href="4c946af49c
">4c946af4</a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/hyperium/hyper/blob/master/CHANGELOG.md">hyper's changelog</a>.</em></p> <blockquote> <h3>v0.14.5 (2021-03-26)</h3> <h4>Bug Fixes</h4> <ul> <li><strong>client:</strong> omit default port from automatic Host headers (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2441">#2441</a>) (<a href="0b11eee9bd
">0b11eee9</a>)</li> <li><strong>headers:</strong> Support multiple Content-Length values on same line (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2471">#2471</a>) (<a href="48fdaf1606
">48fdaf16</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2470">#2470</a>)</li> <li><strong>server:</strong> skip automatic Content-Length headers when not allowed (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2216">#2216</a>) (<a href="8cbf9527df
">8cbf9527</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2215">#2215</a>)</li> </ul> <h4>Features</h4> <ul> <li><strong>client:</strong> allow HTTP/0.9 responses behind a flag (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2473">#2473</a>) (<a href="68d4e4a3db
">68d4e4a3</a>, closes <a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2468">#2468</a>)</li> <li><strong>server:</strong> add <code>AddrIncoming::from_listener</code> constructor (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2439">#2439</a>) (<a href="4c946af49c
">4c946af4</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="98e7e0bd15
"><code>98e7e0b</code></a> v0.14.5</li> <li><a href="895e4cf3fb
"><code>895e4cf</code></a> refactor(ffi): return null ptr instead of aborting in C API (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2478">#2478</a>)</li> <li><a href="68d4e4a3db
"><code>68d4e4a</code></a> feat(client): allow HTTP/0.9 responses behind a flag (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2473">#2473</a>)</li> <li><a href="51ed71b0a6
"><code>51ed71b</code></a> docs(client): use Method::POST to match the example in <a href="https://hyper.rs/guide">https://hyper.rs/guide</a>...</li> <li><a href="41f99578a5
"><code>41f9957</code></a> refactor(dependencies): update to socket2 v0.4.0 (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2472">#2472</a>)</li> <li><a href="48fdaf1606
"><code>48fdaf1</code></a> fix(headers): Support multiple Content-Length values on same line (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2471">#2471</a>)</li> <li><a href="eb0e718696
"><code>eb0e718</code></a> docs(body): add links to to_bytes and aggregate (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2464">#2464</a>)</li> <li><a href="297a068454
"><code>297a068</code></a> docs(examples): upgrade tokio version (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2456">#2456</a>)</li> <li><a href="34085afef6
"><code>34085af</code></a> docs(examples): use hyper v0.14 and full feature (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2451">#2451</a>)</li> <li><a href="8cbf9527df
"><code>8cbf952</code></a> fix(server): skip automatic Content-Length headers when not allowed (<a href="https://github-redirect.dependabot.com/hyperium/hyper/issues/2216">#2216</a>)</li> <li>Additional commits viewable in <a href="https://github.com/hyperium/hyper/compare/v0.14.4...v0.14.5">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hyper&package-manager=cargo&previous-version=0.14.4&new-version=0.14.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Co-authored-by: Thomas Eizinger <thomas@eizinger.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
commit
6fb495b6ab
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -127,4 +127,3 @@ jobs:
|
||||
run: cargo test --package swap --all-features --test ${{ matrix.test_name }} ""
|
||||
env:
|
||||
MONERO_ADDITIONAL_SLEEP_PERIOD: 60000
|
||||
RUST_MIN_STACK: 16777216 # 16 MB. Default is 8MB. This is fine as in tests we start 2 programs: Alice and Bob.
|
||||
|
25
Cargo.lock
generated
25
Cargo.lock
generated
@ -88,9 +88,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
|
||||
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@ -136,17 +136,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.48"
|
||||
@ -1408,9 +1397,9 @@ checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.4"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
|
||||
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -1423,7 +1412,7 @@ dependencies = [
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project 1.0.5",
|
||||
"socket2 0.3.19",
|
||||
"socket2 0.4.0",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@ -2015,7 +2004,6 @@ dependencies = [
|
||||
"testcontainers 0.12.0",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@ -3433,7 +3421,6 @@ version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"atty",
|
||||
"backoff",
|
||||
@ -3484,7 +3471,6 @@ dependencies = [
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid",
|
||||
@ -3874,6 +3860,7 @@ dependencies = [
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -14,5 +14,4 @@ spectral = "0.6"
|
||||
testcontainers = "0.12"
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "time", "macros"] }
|
||||
tracing = "0.1"
|
||||
tracing-log = "0.1"
|
||||
tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter"] }
|
||||
tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter", "tracing-log"] }
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::testutils::init_tracing;
|
||||
use monero_harness::Monero;
|
||||
use spectral::prelude::*;
|
||||
use std::time::Duration;
|
||||
use testcontainers::clients::Cli;
|
||||
use tokio::time;
|
||||
|
||||
mod testutils;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn init_miner_and_mine_to_miner_address() {
|
||||
let _guard = init_tracing();
|
||||
let _guard = tracing_subscriber::fmt()
|
||||
.with_env_filter("warn,test=debug,monero_harness=debug,monero_rpc=debug")
|
||||
.set_default();
|
||||
|
||||
let tc = Cli::default();
|
||||
let (monero, _monerod_container) = Monero::new(&tc, vec![]).await.unwrap();
|
||||
|
@ -1,28 +0,0 @@
|
||||
use tracing::subscriber::DefaultGuard;
|
||||
use tracing_log::LogTracer;
|
||||
|
||||
/// Utility function to initialize logging in the test environment.
|
||||
/// Note that you have to keep the `_guard` in scope after calling in test:
|
||||
///
|
||||
/// ```rust
|
||||
/// let _guard = init_tracing();
|
||||
/// ```
|
||||
pub fn init_tracing() -> DefaultGuard {
|
||||
// converts all log records into tracing events
|
||||
// Note: Make sure to initialize without unwrapping, otherwise this causes
|
||||
// trouble when running multiple tests.
|
||||
let _ = LogTracer::init();
|
||||
|
||||
let global_filter = tracing::Level::WARN;
|
||||
let test_filter = tracing::Level::DEBUG;
|
||||
let monero_harness_filter = tracing::Level::DEBUG;
|
||||
let monero_rpc_filter = tracing::Level::DEBUG;
|
||||
|
||||
use tracing_subscriber::util::SubscriberInitExt as _;
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(format!(
|
||||
"{},test={},monero_harness={},monero_rpc={}",
|
||||
global_filter, test_filter, monero_harness_filter, monero_rpc_filter,
|
||||
))
|
||||
.set_default()
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
use crate::testutils::init_tracing;
|
||||
use monero_harness::{Monero, MoneroWalletRpc};
|
||||
use spectral::prelude::*;
|
||||
use std::time::Duration;
|
||||
use testcontainers::clients::Cli;
|
||||
use tokio::time::sleep;
|
||||
|
||||
mod testutils;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fund_transfer_and_check_tx_key() {
|
||||
let _guard = init_tracing();
|
||||
let _guard = tracing_subscriber::fmt()
|
||||
.with_env_filter("warn,test=debug,monero_harness=debug,monero_rpc=debug")
|
||||
.set_default();
|
||||
|
||||
let fund_alice: u64 = 1_000_000_000_000;
|
||||
let fund_bob = 0;
|
||||
|
@ -488,7 +488,7 @@ struct CheckTxKeyParams {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
pub struct CheckTxKey {
|
||||
pub confirmations: u32,
|
||||
pub confirmations: u64,
|
||||
pub received: u64,
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ name = "swap"
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
async-compression = { version = "0.3", features = ["bzip2", "tokio"] }
|
||||
async-recursion = "0.3"
|
||||
async-trait = "0.1"
|
||||
atty = "0.2"
|
||||
backoff = { version = "0.3", features = ["tokio"] }
|
||||
@ -53,8 +52,7 @@ tokio-util = { version = "0.6", features = ["io"] }
|
||||
toml = "0.5"
|
||||
tracing = { version = "0.1", features = ["attributes"] }
|
||||
tracing-futures = { version = "0.2", features = ["std-future", "futures-03"] }
|
||||
tracing-log = "0.1"
|
||||
tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono"] }
|
||||
tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] }
|
||||
url = { version = "2", features = ["serde"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
void = "1"
|
||||
|
@ -12,7 +12,7 @@ pub struct Config {
|
||||
pub bitcoin_punish_timelock: PunishTimelock,
|
||||
pub bitcoin_network: bitcoin::Network,
|
||||
pub monero_avg_block_time: Duration,
|
||||
pub monero_finality_confirmations: u32,
|
||||
pub monero_finality_confirmations: u64,
|
||||
pub monero_network: monero::Network,
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::Interval;
|
||||
use tracing::{debug, info};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -34,9 +33,9 @@ impl Wallet {
|
||||
"Unable to create Monero wallet, please ensure that the monero-wallet-rpc is available",
|
||||
)?;
|
||||
|
||||
debug!("Created Monero wallet {}", name);
|
||||
tracing::debug!("Created Monero wallet {}", name);
|
||||
} else {
|
||||
debug!("Opened Monero wallet {}", name);
|
||||
tracing::debug!("Opened Monero wallet {}", name);
|
||||
}
|
||||
|
||||
Self::connect(client, name, env_config).await
|
||||
@ -271,7 +270,7 @@ pub struct WatchRequest {
|
||||
pub public_spend_key: PublicKey,
|
||||
pub public_view_key: PublicViewKey,
|
||||
pub transfer_proof: TransferProof,
|
||||
pub conf_target: u32,
|
||||
pub conf_target: u64,
|
||||
pub expected: Amount,
|
||||
}
|
||||
|
||||
@ -280,14 +279,16 @@ async fn wait_for_confirmations<Fut>(
|
||||
fetch_tx: impl Fn(String) -> Fut,
|
||||
mut check_interval: Interval,
|
||||
expected: Amount,
|
||||
conf_target: u32,
|
||||
conf_target: u64,
|
||||
) -> Result<(), InsufficientFunds>
|
||||
where
|
||||
Fut: Future<Output = Result<CheckTxKey>>,
|
||||
{
|
||||
let mut seen_confirmations = 0u32;
|
||||
let mut seen_confirmations = 0u64;
|
||||
|
||||
while seen_confirmations < conf_target {
|
||||
check_interval.tick().await; // tick() at the beginning of the loop so every `continue` tick()s as well
|
||||
|
||||
let tx = match fetch_tx(txid.clone()).await {
|
||||
Ok(proof) => proof,
|
||||
Err(error) => {
|
||||
@ -310,10 +311,8 @@ where
|
||||
|
||||
if tx.confirmations > seen_confirmations {
|
||||
seen_confirmations = tx.confirmations;
|
||||
info!(%txid, "Monero lock tx has {} out of {} confirmations", tx.confirmations, conf_target);
|
||||
tracing::info!(%txid, "Monero lock tx has {} out of {} confirmations", tx.confirmations, conf_target);
|
||||
}
|
||||
|
||||
check_interval.tick().await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -323,7 +322,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use monero_rpc::wallet::CheckTxKey;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tokio::test]
|
||||
@ -364,9 +363,9 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn visual_log_check() {
|
||||
let _ = tracing_subscriber::fmt().with_test_writer().try_init();
|
||||
const MAX_REQUESTS: u32 = 20;
|
||||
const MAX_REQUESTS: u64 = 20;
|
||||
|
||||
let requests = Arc::new(AtomicU32::new(0));
|
||||
let requests = Arc::new(AtomicU64::new(0));
|
||||
|
||||
let result = wait_for_confirmations(
|
||||
String::from("TXID"),
|
||||
|
@ -361,7 +361,7 @@ impl State3 {
|
||||
pub fn lock_xmr_watch_request(
|
||||
&self,
|
||||
transfer_proof: TransferProof,
|
||||
conf_target: u32,
|
||||
conf_target: u64,
|
||||
) -> WatchRequest {
|
||||
let S_a = monero::PublicKey::from_private_key(&monero::PrivateKey { scalar: self.s_a });
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Run an XMR/BTC swap in the role of Alice.
|
||||
//! Alice holds XMR and wishes receive BTC.
|
||||
use crate::bitcoin::{ExpiredTimelocks, TxRedeem};
|
||||
use crate::database::Database;
|
||||
use crate::env::Config;
|
||||
use crate::monero_ext::ScalarExt;
|
||||
use crate::protocol::alice;
|
||||
@ -9,13 +8,10 @@ use crate::protocol::alice::event_loop::EventLoopHandle;
|
||||
use crate::protocol::alice::AliceState;
|
||||
use crate::{bitcoin, database, monero};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_recursion::async_recursion;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::sync::Arc;
|
||||
use tokio::select;
|
||||
use tokio::time::timeout;
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
trait Rng: RngCore + CryptoRng + Send {}
|
||||
|
||||
@ -37,41 +33,40 @@ pub async fn run(swap: alice::Swap) -> Result<AliceState> {
|
||||
|
||||
#[tracing::instrument(name = "swap", skip(swap,is_target_state), fields(id = %swap.swap_id))]
|
||||
pub async fn run_until(
|
||||
swap: alice::Swap,
|
||||
mut swap: alice::Swap,
|
||||
is_target_state: fn(&AliceState) -> bool,
|
||||
) -> Result<AliceState> {
|
||||
run_until_internal(
|
||||
swap.state,
|
||||
is_target_state,
|
||||
swap.event_loop_handle,
|
||||
swap.bitcoin_wallet,
|
||||
swap.monero_wallet,
|
||||
swap.env_config,
|
||||
swap.swap_id,
|
||||
swap.db,
|
||||
)
|
||||
.await
|
||||
}
|
||||
let mut current_state = swap.state;
|
||||
|
||||
// State machine driver for swap execution
|
||||
#[async_recursion]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn run_until_internal(
|
||||
state: AliceState,
|
||||
is_target_state: fn(&AliceState) -> bool,
|
||||
mut event_loop_handle: EventLoopHandle,
|
||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||
monero_wallet: Arc<monero::Wallet>,
|
||||
env_config: Config,
|
||||
swap_id: Uuid,
|
||||
db: Arc<Database>,
|
||||
) -> Result<AliceState> {
|
||||
info!("Current state: {}", state);
|
||||
if is_target_state(&state) {
|
||||
return Ok(state);
|
||||
while !is_target_state(¤t_state) {
|
||||
current_state = next_state(
|
||||
current_state,
|
||||
&mut swap.event_loop_handle,
|
||||
swap.bitcoin_wallet.as_ref(),
|
||||
swap.monero_wallet.as_ref(),
|
||||
&swap.env_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let db_state = (¤t_state).into();
|
||||
swap.db
|
||||
.insert_latest_state(swap.swap_id, database::Swap::Alice(db_state))
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_state = match state {
|
||||
Ok(current_state)
|
||||
}
|
||||
|
||||
async fn next_state(
|
||||
state: AliceState,
|
||||
event_loop_handle: &mut EventLoopHandle,
|
||||
bitcoin_wallet: &bitcoin::Wallet,
|
||||
monero_wallet: &monero::Wallet,
|
||||
env_config: &Config,
|
||||
) -> Result<AliceState> {
|
||||
info!("Current state: {}", state);
|
||||
|
||||
Ok(match state {
|
||||
AliceState::Started { state3 } => {
|
||||
timeout(
|
||||
env_config.bob_time_to_act,
|
||||
@ -121,10 +116,10 @@ async fn run_until_internal(
|
||||
AliceState::XmrLocked {
|
||||
state3,
|
||||
monero_wallet_restore_blockheight,
|
||||
} => match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? {
|
||||
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||
ExpiredTimelocks::None => {
|
||||
select! {
|
||||
_ = state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => {
|
||||
_ = state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet) => {
|
||||
AliceState::CancelTimelockExpired {
|
||||
state3,
|
||||
monero_wallet_restore_blockheight,
|
||||
@ -150,7 +145,7 @@ async fn run_until_internal(
|
||||
state3,
|
||||
encrypted_signature,
|
||||
monero_wallet_restore_blockheight,
|
||||
} => match state3.expired_timelocks(bitcoin_wallet.as_ref()).await? {
|
||||
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||
ExpiredTimelocks::None => {
|
||||
match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete(
|
||||
*encrypted_signature,
|
||||
@ -168,7 +163,7 @@ async fn run_until_internal(
|
||||
Err(e) => {
|
||||
error!("Publishing the redeem transaction failed with {}, attempting to wait for cancellation now. If you restart the application before the timelock is expired publishing the redeem transaction will be retried.", e);
|
||||
state3
|
||||
.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref())
|
||||
.wait_for_cancel_timelock_to_expire(bitcoin_wallet)
|
||||
.await?;
|
||||
|
||||
AliceState::CancelTimelockExpired {
|
||||
@ -180,7 +175,7 @@ async fn run_until_internal(
|
||||
Err(e) => {
|
||||
error!("Constructing the redeem transaction failed with {}, attempting to wait for cancellation now.", e);
|
||||
state3
|
||||
.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref())
|
||||
.wait_for_cancel_timelock_to_expire(bitcoin_wallet)
|
||||
.await?;
|
||||
|
||||
AliceState::CancelTimelockExpired {
|
||||
@ -336,20 +331,5 @@ async fn run_until_internal(
|
||||
AliceState::BtcRedeemed => AliceState::BtcRedeemed,
|
||||
AliceState::BtcPunished => AliceState::BtcPunished,
|
||||
AliceState::SafelyAborted => AliceState::SafelyAborted,
|
||||
};
|
||||
|
||||
let db_state = (&new_state).into();
|
||||
db.insert_latest_state(swap_id, database::Swap::Alice(db_state))
|
||||
.await?;
|
||||
run_until_internal(
|
||||
new_state,
|
||||
is_target_state,
|
||||
event_loop_handle,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
env_config,
|
||||
swap_id,
|
||||
db,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ pub struct State0 {
|
||||
cancel_timelock: CancelTimelock,
|
||||
punish_timelock: PunishTimelock,
|
||||
refund_address: bitcoin::Address,
|
||||
min_monero_confirmations: u32,
|
||||
min_monero_confirmations: u64,
|
||||
}
|
||||
|
||||
impl State0 {
|
||||
@ -94,7 +94,7 @@ impl State0 {
|
||||
cancel_timelock: CancelTimelock,
|
||||
punish_timelock: PunishTimelock,
|
||||
refund_address: bitcoin::Address,
|
||||
min_monero_confirmations: u32,
|
||||
min_monero_confirmations: u64,
|
||||
) -> Self {
|
||||
let b = bitcoin::SecretKey::new_random(rng);
|
||||
|
||||
@ -185,7 +185,7 @@ pub struct State1 {
|
||||
redeem_address: bitcoin::Address,
|
||||
punish_address: bitcoin::Address,
|
||||
tx_lock: bitcoin::TxLock,
|
||||
min_monero_confirmations: u32,
|
||||
min_monero_confirmations: u64,
|
||||
}
|
||||
|
||||
impl State1 {
|
||||
@ -245,7 +245,7 @@ pub struct State2 {
|
||||
tx_lock: bitcoin::TxLock,
|
||||
tx_cancel_sig_a: Signature,
|
||||
tx_refund_encsig: bitcoin::EncryptedSignature,
|
||||
min_monero_confirmations: u32,
|
||||
min_monero_confirmations: u64,
|
||||
}
|
||||
|
||||
impl State2 {
|
||||
@ -302,7 +302,7 @@ pub struct State3 {
|
||||
tx_lock: bitcoin::TxLock,
|
||||
tx_cancel_sig_a: Signature,
|
||||
tx_refund_encsig: bitcoin::EncryptedSignature,
|
||||
min_monero_confirmations: u32,
|
||||
min_monero_confirmations: u64,
|
||||
}
|
||||
|
||||
impl State3 {
|
||||
@ -485,23 +485,17 @@ pub struct State5 {
|
||||
s_b: monero::Scalar,
|
||||
v: monero::PrivateViewKey,
|
||||
tx_lock: bitcoin::TxLock,
|
||||
monero_wallet_restore_blockheight: BlockHeight,
|
||||
pub monero_wallet_restore_blockheight: BlockHeight,
|
||||
}
|
||||
|
||||
impl State5 {
|
||||
pub async fn claim_xmr(&self, monero_wallet: &monero::Wallet) -> Result<()> {
|
||||
pub fn xmr_keys(&self) -> (monero::PrivateKey, monero::PrivateViewKey) {
|
||||
let s_b = monero::PrivateKey { scalar: self.s_b };
|
||||
|
||||
let s = self.s_a + s_b;
|
||||
|
||||
// NOTE: This actually generates and opens a new wallet, closing the currently
|
||||
// open one.
|
||||
monero_wallet
|
||||
.create_from_and_load(s, self.v, self.monero_wallet_restore_blockheight)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
(s, self.v)
|
||||
}
|
||||
|
||||
pub fn tx_lock_id(&self) -> bitcoin::Txid {
|
||||
self.tx_lock.txid()
|
||||
}
|
||||
|
@ -1,17 +1,14 @@
|
||||
use crate::bitcoin::ExpiredTimelocks;
|
||||
use crate::database::{Database, Swap};
|
||||
use crate::database::Swap;
|
||||
use crate::env::Config;
|
||||
use crate::protocol::bob;
|
||||
use crate::protocol::bob::event_loop::EventLoopHandle;
|
||||
use crate::protocol::bob::state::*;
|
||||
use crate::{bitcoin, monero};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_recursion::async_recursion;
|
||||
use rand::rngs::OsRng;
|
||||
use std::sync::Arc;
|
||||
use tokio::select;
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn is_complete(state: &BobState) -> bool {
|
||||
matches!(
|
||||
@ -29,49 +26,48 @@ pub async fn run(swap: bob::Swap) -> Result<BobState> {
|
||||
}
|
||||
|
||||
pub async fn run_until(
|
||||
swap: bob::Swap,
|
||||
mut swap: bob::Swap,
|
||||
is_target_state: fn(&BobState) -> bool,
|
||||
) -> Result<BobState> {
|
||||
run_until_internal(
|
||||
swap.state,
|
||||
is_target_state,
|
||||
swap.event_loop_handle,
|
||||
swap.db,
|
||||
swap.bitcoin_wallet,
|
||||
swap.monero_wallet,
|
||||
swap.swap_id,
|
||||
swap.env_config,
|
||||
swap.receive_monero_address,
|
||||
)
|
||||
.await
|
||||
let mut current_state = swap.state;
|
||||
|
||||
while !is_target_state(¤t_state) {
|
||||
current_state = next_state(
|
||||
current_state,
|
||||
&mut swap.event_loop_handle,
|
||||
swap.bitcoin_wallet.as_ref(),
|
||||
swap.monero_wallet.as_ref(),
|
||||
&swap.env_config,
|
||||
swap.receive_monero_address,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let db_state = current_state.clone().into();
|
||||
swap.db
|
||||
.insert_latest_state(swap.swap_id, Swap::Bob(db_state))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(current_state)
|
||||
}
|
||||
|
||||
// State machine driver for swap execution
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[async_recursion]
|
||||
async fn run_until_internal(
|
||||
async fn next_state(
|
||||
state: BobState,
|
||||
is_target_state: fn(&BobState) -> bool,
|
||||
mut event_loop_handle: EventLoopHandle,
|
||||
db: Database,
|
||||
bitcoin_wallet: Arc<bitcoin::Wallet>,
|
||||
monero_wallet: Arc<monero::Wallet>,
|
||||
swap_id: Uuid,
|
||||
env_config: Config,
|
||||
event_loop_handle: &mut EventLoopHandle,
|
||||
bitcoin_wallet: &bitcoin::Wallet,
|
||||
monero_wallet: &monero::Wallet,
|
||||
env_config: &Config,
|
||||
receive_monero_address: monero::Address,
|
||||
) -> Result<BobState> {
|
||||
trace!("Current state: {}", state);
|
||||
if is_target_state(&state) {
|
||||
return Ok(state);
|
||||
}
|
||||
|
||||
let new_state = match state {
|
||||
Ok(match state {
|
||||
BobState::Started { btc_amount } => {
|
||||
let bitcoin_refund_address = bitcoin_wallet.new_address().await?;
|
||||
|
||||
let state2 = request_price_and_setup(
|
||||
btc_amount,
|
||||
&mut event_loop_handle,
|
||||
event_loop_handle,
|
||||
env_config,
|
||||
bitcoin_refund_address,
|
||||
)
|
||||
@ -93,10 +89,10 @@ async fn run_until_internal(
|
||||
// Bob has locked Btc
|
||||
// Watch for Alice to Lock Xmr or for cancel timelock to elapse
|
||||
BobState::BtcLocked(state3) => {
|
||||
if let ExpiredTimelocks::None = state3.current_epoch(bitcoin_wallet.as_ref()).await? {
|
||||
if let ExpiredTimelocks::None = state3.current_epoch(bitcoin_wallet).await? {
|
||||
let transfer_proof_watcher = event_loop_handle.recv_transfer_proof();
|
||||
let cancel_timelock_expires =
|
||||
state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref());
|
||||
state3.wait_for_cancel_timelock_to_expire(bitcoin_wallet);
|
||||
|
||||
// Record the current monero wallet block height so we don't have to scan from
|
||||
// block 0 once we create the redeem wallet.
|
||||
@ -133,7 +129,7 @@ async fn run_until_internal(
|
||||
lock_transfer_proof,
|
||||
monero_wallet_restore_blockheight,
|
||||
} => {
|
||||
if let ExpiredTimelocks::None = state.current_epoch(bitcoin_wallet.as_ref()).await? {
|
||||
if let ExpiredTimelocks::None = state.current_epoch(bitcoin_wallet).await? {
|
||||
let watch_request = state.lock_xmr_watch_request(lock_transfer_proof);
|
||||
|
||||
select! {
|
||||
@ -142,13 +138,13 @@ async fn run_until_internal(
|
||||
Ok(()) => BobState::XmrLocked(state.xmr_locked(monero_wallet_restore_blockheight)),
|
||||
Err(e) => {
|
||||
tracing::warn!("Waiting for refund because insufficient Monero have been locked! {}", e);
|
||||
state.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()).await?;
|
||||
state.wait_for_cancel_timelock_to_expire(bitcoin_wallet).await?;
|
||||
|
||||
BobState::CancelTimelockExpired(state.cancel())
|
||||
},
|
||||
}
|
||||
}
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => {
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet) => {
|
||||
BobState::CancelTimelockExpired(state.cancel())
|
||||
}
|
||||
}
|
||||
@ -157,7 +153,7 @@ async fn run_until_internal(
|
||||
}
|
||||
}
|
||||
BobState::XmrLocked(state) => {
|
||||
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet.as_ref()).await? {
|
||||
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? {
|
||||
// Alice has locked Xmr
|
||||
// Bob sends Alice his key
|
||||
|
||||
@ -165,7 +161,7 @@ async fn run_until_internal(
|
||||
_ = event_loop_handle.send_encrypted_signature(state.tx_redeem_encsig()) => {
|
||||
BobState::EncSigSent(state)
|
||||
},
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => {
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet) => {
|
||||
BobState::CancelTimelockExpired(state.cancel())
|
||||
}
|
||||
}
|
||||
@ -174,12 +170,12 @@ async fn run_until_internal(
|
||||
}
|
||||
}
|
||||
BobState::EncSigSent(state) => {
|
||||
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet.as_ref()).await? {
|
||||
if let ExpiredTimelocks::None = state.expired_timelock(bitcoin_wallet).await? {
|
||||
select! {
|
||||
state5 = state.watch_for_redeem_btc(bitcoin_wallet.as_ref()) => {
|
||||
state5 = state.watch_for_redeem_btc(bitcoin_wallet) => {
|
||||
BobState::BtcRedeemed(state5?)
|
||||
},
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet.as_ref()) => {
|
||||
_ = state.wait_for_cancel_timelock_to_expire(bitcoin_wallet) => {
|
||||
BobState::CancelTimelockExpired(state.cancel())
|
||||
}
|
||||
}
|
||||
@ -188,8 +184,13 @@ async fn run_until_internal(
|
||||
}
|
||||
}
|
||||
BobState::BtcRedeemed(state) => {
|
||||
// Bob redeems XMR using revealed s_a
|
||||
state.claim_xmr(monero_wallet.as_ref()).await?;
|
||||
let (spend_key, view_key) = state.xmr_keys();
|
||||
|
||||
// NOTE: This actually generates and opens a new wallet, closing the currently
|
||||
// open one.
|
||||
monero_wallet
|
||||
.create_from_and_load(spend_key, view_key, state.monero_wallet_restore_blockheight)
|
||||
.await?;
|
||||
|
||||
// Ensure that the generated wallet is synced so we have a proper balance
|
||||
monero_wallet.refresh().await?;
|
||||
@ -205,26 +206,22 @@ async fn run_until_internal(
|
||||
}
|
||||
}
|
||||
BobState::CancelTimelockExpired(state4) => {
|
||||
if state4
|
||||
.check_for_tx_cancel(bitcoin_wallet.as_ref())
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
state4.submit_tx_cancel(bitcoin_wallet.as_ref()).await?;
|
||||
if state4.check_for_tx_cancel(bitcoin_wallet).await.is_err() {
|
||||
state4.submit_tx_cancel(bitcoin_wallet).await?;
|
||||
}
|
||||
|
||||
BobState::BtcCancelled(state4)
|
||||
}
|
||||
BobState::BtcCancelled(state) => {
|
||||
// Bob has cancelled the swap
|
||||
match state.expired_timelock(bitcoin_wallet.as_ref()).await? {
|
||||
match state.expired_timelock(bitcoin_wallet).await? {
|
||||
ExpiredTimelocks::None => {
|
||||
bail!(
|
||||
"Internal error: canceled state reached before cancel timelock was expired"
|
||||
);
|
||||
}
|
||||
ExpiredTimelocks::Cancel => {
|
||||
state.refund_btc(bitcoin_wallet.as_ref()).await?;
|
||||
state.refund_btc(bitcoin_wallet).await?;
|
||||
BobState::BtcRefunded(state)
|
||||
}
|
||||
ExpiredTimelocks::Punish => BobState::BtcPunished {
|
||||
@ -236,28 +233,13 @@ async fn run_until_internal(
|
||||
BobState::BtcPunished { tx_lock_id } => BobState::BtcPunished { tx_lock_id },
|
||||
BobState::SafelyAborted => BobState::SafelyAborted,
|
||||
BobState::XmrRedeemed { tx_lock_id } => BobState::XmrRedeemed { tx_lock_id },
|
||||
};
|
||||
|
||||
let db_state = new_state.clone().into();
|
||||
db.insert_latest_state(swap_id, Swap::Bob(db_state)).await?;
|
||||
run_until_internal(
|
||||
new_state,
|
||||
is_target_state,
|
||||
event_loop_handle,
|
||||
db,
|
||||
bitcoin_wallet,
|
||||
monero_wallet,
|
||||
swap_id,
|
||||
env_config,
|
||||
receive_monero_address,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn request_price_and_setup(
|
||||
btc: bitcoin::Amount,
|
||||
event_loop_handle: &mut EventLoopHandle,
|
||||
env_config: Config,
|
||||
env_config: &Config,
|
||||
bitcoin_refund_address: bitcoin::Address,
|
||||
) -> Result<bob::state::State2> {
|
||||
let xmr = event_loop_handle.request_spot_price(btc).await?;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
@ -8,9 +7,6 @@ pub fn init_tracing(level: LevelFilter) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// We want upstream library log messages, just only at Info level.
|
||||
LogTracer::init_with_filter(tracing_log::log::LevelFilter::Info)?;
|
||||
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
|
||||
let builder = FmtSubscriber::builder()
|
||||
|
@ -2,13 +2,16 @@ mod bitcoind;
|
||||
mod electrs;
|
||||
|
||||
use crate::testutils;
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use bitcoin_harness::{BitcoindRpcApi, Client};
|
||||
use futures::Future;
|
||||
use get_port::get_port;
|
||||
use libp2p::core::Multiaddr;
|
||||
use libp2p::{PeerId, Swarm};
|
||||
use monero_harness::{image, Monero};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -28,8 +31,7 @@ use testcontainers::{Container, Docker, RunArgs};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::interval;
|
||||
use tracing::dispatcher::DefaultGuard;
|
||||
use tracing_log::LogTracer;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -148,108 +150,83 @@ impl TestContext {
|
||||
pub async fn assert_alice_redeemed(&mut self, state: AliceState) {
|
||||
assert!(matches!(state, AliceState::BtcRedeemed));
|
||||
|
||||
self.alice_bitcoin_wallet.sync().await.unwrap();
|
||||
assert_eventual_balance(
|
||||
self.alice_bitcoin_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.alice_redeemed_btc_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
self.alice_starting_balances.btc + self.btc_amount
|
||||
- bitcoin::Amount::from_sat(bitcoin::TX_FEE)
|
||||
);
|
||||
|
||||
let xmr_balance_after_swap = self
|
||||
.alice_monero_wallet
|
||||
.as_ref()
|
||||
.get_balance()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.xmr_amount,
|
||||
"{} !< {} - {}",
|
||||
xmr_balance_after_swap,
|
||||
self.alice_starting_balances.xmr,
|
||||
self.xmr_amount
|
||||
);
|
||||
assert_eventual_balance(
|
||||
self.alice_monero_wallet.as_ref(),
|
||||
Ordering::Less,
|
||||
self.alice_redeemed_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn assert_alice_refunded(&mut self, state: AliceState) {
|
||||
assert!(matches!(state, AliceState::XmrRefunded));
|
||||
|
||||
self.alice_bitcoin_wallet.sync().await.unwrap();
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(btc_balance_after_swap, self.alice_starting_balances.btc);
|
||||
|
||||
// Ensure that Alice's balance is refreshed as we use a newly created wallet
|
||||
self.alice_monero_wallet.as_ref().refresh().await.unwrap();
|
||||
let xmr_balance_after_swap = self
|
||||
.alice_monero_wallet
|
||||
.as_ref()
|
||||
.get_balance()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eventual_balance(
|
||||
self.alice_bitcoin_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.alice_refunded_btc_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice pays fees - comparison does not take exact lock fee into account
|
||||
assert!(
|
||||
xmr_balance_after_swap > self.alice_starting_balances.xmr - self.xmr_amount,
|
||||
"{} > {} - {}",
|
||||
xmr_balance_after_swap,
|
||||
self.alice_starting_balances.xmr,
|
||||
self.xmr_amount
|
||||
);
|
||||
assert_eventual_balance(
|
||||
self.alice_monero_wallet.as_ref(),
|
||||
Ordering::Greater,
|
||||
self.alice_refunded_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn assert_alice_punished(&self, state: AliceState) {
|
||||
assert!(matches!(state, AliceState::BtcPunished));
|
||||
|
||||
self.alice_bitcoin_wallet.sync().await.unwrap();
|
||||
assert_eventual_balance(
|
||||
self.alice_bitcoin_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.alice_punished_btc_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
self.alice_starting_balances.btc + self.btc_amount
|
||||
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE)
|
||||
);
|
||||
|
||||
let xmr_balance_after_swap = self
|
||||
.alice_monero_wallet
|
||||
.as_ref()
|
||||
.get_balance()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(xmr_balance_after_swap <= self.alice_starting_balances.xmr - self.xmr_amount);
|
||||
assert_eventual_balance(
|
||||
self.alice_monero_wallet.as_ref(),
|
||||
Ordering::Less,
|
||||
self.alice_punished_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn assert_bob_redeemed(&self, state: BobState) {
|
||||
self.bob_bitcoin_wallet.sync().await.unwrap();
|
||||
|
||||
let lock_tx_id = if let BobState::XmrRedeemed { tx_lock_id } = state {
|
||||
tx_lock_id
|
||||
} else {
|
||||
panic!("Bob in not in xmr redeemed state: {:?}", state);
|
||||
};
|
||||
|
||||
let lock_tx_bitcoin_fee = self
|
||||
.bob_bitcoin_wallet
|
||||
.transaction_fee(lock_tx_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee
|
||||
);
|
||||
assert_eventual_balance(
|
||||
self.bob_bitcoin_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.bob_redeemed_btc_balance(state).await.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// unload the generated wallet by opening the original wallet
|
||||
self.bob_monero_wallet.re_open().await.unwrap();
|
||||
// refresh the original wallet to make sure the balance is caught up
|
||||
self.bob_monero_wallet.refresh().await.unwrap();
|
||||
|
||||
// Ensure that Bob's balance is refreshed as we use a newly created wallet
|
||||
self.bob_monero_wallet.as_ref().refresh().await.unwrap();
|
||||
let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
|
||||
assert!(xmr_balance_after_swap > self.bob_starting_balances.xmr);
|
||||
assert_eventual_balance(
|
||||
self.bob_monero_wallet.as_ref(),
|
||||
Ordering::Greater,
|
||||
self.bob_redeemed_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn assert_bob_refunded(&self, state: BobState) {
|
||||
@ -266,7 +243,7 @@ impl TestContext {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
let btc_balance_after_swap = self.bob_bitcoin_wallet.balance().await.unwrap();
|
||||
|
||||
let alice_submitted_cancel = btc_balance_after_swap
|
||||
== self.bob_starting_balances.btc
|
||||
@ -282,33 +259,181 @@ impl TestContext {
|
||||
// Since we cannot be sure who submitted it we have to assert accordingly
|
||||
assert!(alice_submitted_cancel || bob_submitted_cancel);
|
||||
|
||||
let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
|
||||
assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr);
|
||||
assert_eventual_balance(
|
||||
self.bob_monero_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.bob_refunded_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub async fn assert_bob_punished(&self, state: BobState) {
|
||||
self.bob_bitcoin_wallet.sync().await.unwrap();
|
||||
assert_eventual_balance(
|
||||
self.bob_bitcoin_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.bob_punished_btc_balance(state).await.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eventual_balance(
|
||||
self.bob_monero_wallet.as_ref(),
|
||||
Ordering::Equal,
|
||||
self.bob_punished_xmr_balance(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn alice_redeemed_xmr_balance(&self) -> monero::Amount {
|
||||
self.alice_starting_balances.xmr - self.xmr_amount
|
||||
}
|
||||
|
||||
fn alice_redeemed_btc_balance(&self) -> bitcoin::Amount {
|
||||
self.alice_starting_balances.btc + self.btc_amount
|
||||
- bitcoin::Amount::from_sat(bitcoin::TX_FEE)
|
||||
}
|
||||
|
||||
fn bob_redeemed_xmr_balance(&self) -> monero::Amount {
|
||||
self.bob_starting_balances.xmr
|
||||
}
|
||||
|
||||
async fn bob_redeemed_btc_balance(&self, state: BobState) -> Result<bitcoin::Amount> {
|
||||
self.bob_bitcoin_wallet.sync().await?;
|
||||
|
||||
let lock_tx_id = if let BobState::XmrRedeemed { tx_lock_id } = state {
|
||||
tx_lock_id
|
||||
} else {
|
||||
bail!("Bob in not in xmr redeemed state: {:?}", state);
|
||||
};
|
||||
|
||||
let lock_tx_bitcoin_fee = self.bob_bitcoin_wallet.transaction_fee(lock_tx_id).await?;
|
||||
|
||||
Ok(self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee)
|
||||
}
|
||||
|
||||
fn alice_refunded_xmr_balance(&self) -> monero::Amount {
|
||||
self.alice_starting_balances.xmr - self.xmr_amount
|
||||
}
|
||||
|
||||
fn alice_refunded_btc_balance(&self) -> bitcoin::Amount {
|
||||
self.alice_starting_balances.btc
|
||||
}
|
||||
|
||||
fn bob_refunded_xmr_balance(&self) -> monero::Amount {
|
||||
self.bob_starting_balances.xmr
|
||||
}
|
||||
|
||||
fn alice_punished_xmr_balance(&self) -> monero::Amount {
|
||||
self.alice_starting_balances.xmr - self.xmr_amount
|
||||
}
|
||||
|
||||
fn alice_punished_btc_balance(&self) -> bitcoin::Amount {
|
||||
self.alice_starting_balances.btc + self.btc_amount
|
||||
- bitcoin::Amount::from_sat(2 * bitcoin::TX_FEE)
|
||||
}
|
||||
|
||||
fn bob_punished_xmr_balance(&self) -> monero::Amount {
|
||||
self.bob_starting_balances.xmr
|
||||
}
|
||||
|
||||
async fn bob_punished_btc_balance(&self, state: BobState) -> Result<bitcoin::Amount> {
|
||||
self.bob_bitcoin_wallet.sync().await?;
|
||||
|
||||
let lock_tx_id = if let BobState::BtcPunished { tx_lock_id } = state {
|
||||
tx_lock_id
|
||||
} else {
|
||||
panic!("Bob in not in btc punished state: {:?}", state);
|
||||
bail!("Bob in not in btc punished state: {:?}", state);
|
||||
};
|
||||
|
||||
let lock_tx_bitcoin_fee = self
|
||||
.bob_bitcoin_wallet
|
||||
.transaction_fee(lock_tx_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let lock_tx_bitcoin_fee = self.bob_bitcoin_wallet.transaction_fee(lock_tx_id).await?;
|
||||
|
||||
let btc_balance_after_swap = self.bob_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee
|
||||
Ok(self.bob_starting_balances.btc - self.btc_amount - lock_tx_bitcoin_fee)
|
||||
}
|
||||
}
|
||||
|
||||
async fn assert_eventual_balance<A: fmt::Display + PartialOrd>(
|
||||
wallet: &impl Wallet<Amount = A>,
|
||||
ordering: Ordering,
|
||||
expected: A,
|
||||
) -> Result<()> {
|
||||
let ordering_str = match ordering {
|
||||
Ordering::Less => "less than",
|
||||
Ordering::Equal => "equal to",
|
||||
Ordering::Greater => "greater than",
|
||||
};
|
||||
|
||||
let mut current_balance = wallet.get_balance().await?;
|
||||
|
||||
let assertion = async {
|
||||
while current_balance.partial_cmp(&expected).unwrap() != ordering {
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
|
||||
wallet.refresh().await?;
|
||||
current_balance = wallet.get_balance().await?;
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"Assertion successful! Balance {} is {} {}",
|
||||
current_balance,
|
||||
ordering_str,
|
||||
expected
|
||||
);
|
||||
|
||||
let xmr_balance_after_swap = self.bob_monero_wallet.as_ref().get_balance().await.unwrap();
|
||||
assert_eq!(xmr_balance_after_swap, self.bob_starting_balances.xmr);
|
||||
Result::<_, anyhow::Error>::Ok(())
|
||||
};
|
||||
|
||||
let timeout = Duration::from_secs(10);
|
||||
|
||||
tokio::time::timeout(timeout, assertion)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Expected balance to be {} {} after at most {}s but was {}",
|
||||
ordering_str,
|
||||
expected,
|
||||
timeout.as_secs(),
|
||||
current_balance
|
||||
)
|
||||
})??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
trait Wallet {
|
||||
type Amount;
|
||||
|
||||
async fn refresh(&self) -> Result<()>;
|
||||
async fn get_balance(&self) -> Result<Self::Amount>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Wallet for monero::Wallet {
|
||||
type Amount = monero::Amount;
|
||||
|
||||
async fn refresh(&self) -> Result<()> {
|
||||
self.refresh().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_balance(&self) -> Result<Self::Amount> {
|
||||
self.get_balance().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Wallet for bitcoin::Wallet {
|
||||
type Amount = bitcoin::Amount;
|
||||
|
||||
async fn refresh(&self) -> Result<()> {
|
||||
self.sync().await
|
||||
}
|
||||
|
||||
async fn get_balance(&self) -> Result<Self::Amount> {
|
||||
self.balance().await
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,7 +445,10 @@ where
|
||||
{
|
||||
let cli = Cli::default();
|
||||
|
||||
let _guard = init_tracing();
|
||||
let _guard = tracing_subscriber::fmt()
|
||||
.with_env_filter("warn,swap=debug,monero_harness=debug,monero_rpc=info,bitcoin_harness=info,testcontainers=info")
|
||||
.with_test_writer()
|
||||
.set_default();
|
||||
|
||||
let env_config = C::get_config();
|
||||
|
||||
@ -659,24 +787,6 @@ struct Containers<'a> {
|
||||
electrs: Container<'a, Cli, electrs::Electrs>,
|
||||
}
|
||||
|
||||
/// Utility function to initialize logging in the test environment.
|
||||
/// Note that you have to keep the `_guard` in scope after calling in test:
|
||||
///
|
||||
/// ```rust
|
||||
/// let _guard = init_tracing();
|
||||
/// ```
|
||||
pub fn init_tracing() -> DefaultGuard {
|
||||
// converts all log records into tracing events
|
||||
// Note: Make sure to initialize without unwrapping, otherwise this causes
|
||||
// trouble when running multiple tests.
|
||||
let _ = LogTracer::init();
|
||||
|
||||
use tracing_subscriber::util::SubscriberInitExt as _;
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter("warn,swap=debug,monero_harness=debug,monero_rpc=info,bitcoin_harness=info,testcontainers=info")
|
||||
.set_default()
|
||||
}
|
||||
|
||||
pub mod alice_run_until {
|
||||
use swap::protocol::alice::AliceState;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user