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:
bors[bot] 2021-03-29 22:31:43 +00:00 committed by GitHub
commit 6fb495b6ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 355 additions and 339 deletions

View file

@ -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,
}

View file

@ -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"),

View file

@ -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 });

View file

@ -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(&current_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 = (&current_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
})
}

View file

@ -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()
}

View file

@ -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(&current_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?;

View file

@ -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()