From cca0d0027e982af00f64aee1835f07f8243a777b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:09:33 +0000 Subject: [PATCH 01/64] build(deps): bump proptest from 1.4.0 to 1.5.0 Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: proptest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 203c718d..b96308c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3144,9 +3144,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", From dabe1abb216378b1644457fa10d4e38c09734421 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:10:02 +0000 Subject: [PATCH 02/64] build(deps): bump uuid from 1.8.0 to 1.9.0 Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/1.8.0...1.9.0) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- swap/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 203c718d..18870f31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5321,9 +5321,9 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "3ea73390fe27785838dcbf75b91b1d84799e28f1ce71e6f372a5dc2200c80de5" dependencies = [ "getrandom 0.2.11", "serde", diff --git a/swap/Cargo.toml b/swap/Cargo.toml index fd2b0b39..cbb7a802 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -67,7 +67,7 @@ tracing-appender = "0.2" tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] } tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "ansi", "env-filter", "time", "tracing-log", "json" ] } url = { version = "2", features = [ "serde" ] } -uuid = { version = "1.8", features = [ "serde", "v4" ] } +uuid = { version = "1.9", features = [ "serde", "v4" ] } void = "1" [target.'cfg(not(windows))'.dependencies] From 2068a4cf787e6b2277d7469bad90804191779f4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:12:44 +0000 Subject: [PATCH 03/64] build(deps): bump strum from 0.26.2 to 0.26.3 Bumps [strum](https://github.com/Peternator7/strum) from 0.26.2 to 0.26.3. - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/compare/v0.26.2...v0.26.3) --- updated-dependencies: - dependency-name: strum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 203c718d..1b9d0b5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1602,6 +1602,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.18" @@ -4475,20 +4481,20 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.1" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", From 3fc934a189baa8a2dede7bf7f57a2bfc4ee7780a Mon Sep 17 00:00:00 2001 From: Ian McKenzie Date: Mon, 24 Jun 2024 21:27:13 -0700 Subject: [PATCH 04/64] Add concurrent_bobs_after_xmr_lock_proof_sent test to ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b3fdbf3..2b0a0ce6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,6 +163,7 @@ jobs: alice_refunds_after_restart_bob_refunded, ensure_same_swap_id, concurrent_bobs_before_xmr_lock_proof_sent, + concurrent_bobs_after_xmr_lock_proof_sent, alice_manually_redeems_after_enc_sig_learned, happy_path_bob_offline_while_alice_redeems_btc, ] From fbf1c3dabfb4105d33accdbff74da00708e9738d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:48:33 +0000 Subject: [PATCH 05/64] build(deps): bump serde_json from 1.0.117 to 1.0.118 Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.117 to 1.0.118. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.117...v1.0.118) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2277173..ed0db29d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4035,9 +4035,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", From 5551f513d562ae09431b05224a13b184e2738542 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:49:47 +0000 Subject: [PATCH 06/64] build(deps): bump uuid from 1.9.0 to 1.9.1 Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/1.9.0...1.9.1) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2277173..677345d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5327,9 +5327,9 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "uuid" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea73390fe27785838dcbf75b91b1d84799e28f1ce71e6f372a5dc2200c80de5" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom 0.2.11", "serde", From 4f8907a4b0cebf81b5c29260ce2d97f442520d0c Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:08:58 +0300 Subject: [PATCH 07/64] prettify error, when bob tries to cancel and refund after disconnecting --- swap/src/cli/cancel_and_refund.rs | 42 +++++++++++++++++++------------ swap/src/protocol/bob/state.rs | 16 ++++++------ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index d542b7ed..bf3a6fc4 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,3 @@ -use crate::bitcoin::wallet::Subscription; use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; @@ -12,8 +11,15 @@ pub async fn cancel_and_refund( bitcoin_wallet: Arc, db: Arc, ) -> Result { - if let Err(err) = cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { - tracing::info!(%err, "Could not submit cancel transaction"); + match cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { + Ok((_, state)) => { + if matches!(state, BobState::BtcCancelled { .. }) { + return Ok(state); + } + } + Err(err) => { + tracing::info!(%err, "Could not submit cancel transaction"); + } }; let state = match refund(swap_id, bitcoin_wallet, db).await { @@ -29,7 +35,7 @@ pub async fn cancel( swap_id: Uuid, bitcoin_wallet: Arc, db: Arc, -) -> Result<(Txid, Subscription, BobState)> { +) -> Result<(Txid, BobState)> { let state = db.get_state(swap_id).await?.try_into()?; let state6 = match state { @@ -55,26 +61,30 @@ pub async fn cancel( tracing::info!(%swap_id, "Manually cancelling swap"); - let (txid, subscription) = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await { - Ok(txid) => txid, + match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await { + Ok((txid, _)) => { + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + return Ok((txid, state)); + } Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); - if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { - tracing::info!("Cancel transaction has already been confirmed on chain"); - } else if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - tracing::info!("General error trying to submit cancel transaction"); + if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) + || error_code == i64::from(RpcErrorCode::RpcVerifyError) + { + let txid = state6.construct_tx_cancel().unwrap().txid(); + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice"); + return Ok((txid, state)); } } bail!(err); } }; - - let state = BobState::BtcCancelled(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - - Ok((txid, subscription, state)) } pub async fn refund( diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 58c1d723..a1fd750a 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -663,12 +663,8 @@ impl State6 { Ok(tx) } - - pub async fn submit_tx_cancel( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result<(Txid, Subscription)> { - let transaction = bitcoin::TxCancel::new( + pub fn construct_tx_cancel(&self) -> Result { + bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, self.A, @@ -676,7 +672,13 @@ impl State6 { self.tx_cancel_fee, )? .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) - .context("Failed to complete Bitcoin cancel transaction")?; + .context("Failed to complete Bitcoin cancel transaction") + } + pub async fn submit_tx_cancel( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result<(Txid, Subscription)> { + let transaction = self.construct_tx_cancel()?; let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; From 8e4fd2cc3b360151ff4d7a3af740d2415378c978 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:46:42 +0300 Subject: [PATCH 08/64] Add test and rustify code --- swap/src/cli/cancel_and_refund.rs | 7 +- ...punishes_after_bob_dead_and_bob_cancels.rs | 83 +++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index bf3a6fc4..b1c0f2aa 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -12,14 +12,13 @@ pub async fn cancel_and_refund( db: Arc, ) -> Result { match cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { - Ok((_, state)) => { - if matches!(state, BobState::BtcCancelled { .. }) { - return Ok(state); - } + Ok((_, state @ BobState::BtcCancelled {..})) => { + return Ok(state); } Err(err) => { tracing::info!(%err, "Could not submit cancel transaction"); } + _ => {} }; let state = match refund(swap_id, bitcoin_wallet, db).await { diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs new file mode 100644 index 00000000..de0ba4e7 --- /dev/null +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -0,0 +1,83 @@ +pub mod harness; + +use harness::alice_run_until::is_xmr_lock_transaction_sent; +use harness::bob_run_until::is_btc_locked; +use harness::FastPunishConfig; +use swap::asb; +use swap::asb::FixedRate; +use swap::cli; +use swap::protocol::alice::AliceState; +use swap::protocol::bob::BobState; +use swap::protocol::{alice, bob}; +/// Bob locks Btc and Alice locks Xmr. Bob does not act; he fails to send Alice +/// the encsig and fail to refund or redeem. Alice punishes using the cancel and +/// punish command. Then Bob tries to refund. +#[tokio::test] +async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { + harness::setup_test(FastPunishConfig, |mut ctx| async move { + let (bob_swap, bob_join_handle) = ctx.bob_swap().await; + let bob_swap_id = bob_swap.id; + let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked)); + + let alice_swap = ctx.alice_next_swap().await; + let alice_bitcoin_wallet = alice_swap.bitcoin_wallet.clone(); + + let alice_swap = tokio::spawn(alice::run_until( + alice_swap, + is_xmr_lock_transaction_sent, + FixedRate::default(), + )); + + let bob_state = bob_swap.await??; + assert!(matches!(bob_state, BobState::BtcLocked { .. })); + + let alice_state = alice_swap.await??; + + // Ensure cancel timelock is expired + if let AliceState::XmrLockTransactionSent { state3, .. } = alice_state { + alice_bitcoin_wallet + .subscribe_to(state3.tx_lock) + .await + .wait_until_confirmed_with(state3.cancel_timelock) + .await?; + } else { + panic!("Alice in unexpected state {}", alice_state); + } + + // manual cancel (required to be able to punish) + + ctx.restart_alice().await; + let alice_swap = ctx.alice_next_swap().await; + let (_, alice_state) = + asb::cancel(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await?; + + // Ensure punish timelock is expired + if let AliceState::BtcCancelled { state3, .. } = alice_state { + alice_bitcoin_wallet + .subscribe_to(state3.tx_cancel()) + .await + .wait_until_confirmed_with(state3.punish_timelock) + .await?; + } else { + panic!("Alice in unexpected state {}", alice_state); + } + + // manual punish + + ctx.restart_alice().await; + let alice_swap = ctx.alice_next_swap().await; + let (_, alice_state) = + asb::punish(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await?; + ctx.assert_alice_punished(alice_state).await; + + let (bob_swap, _) = ctx + .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) + .await; + + let state = + cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; + assert!(matches!(state, BobState::BtcCancelled { .. })); + Ok(()) + }) + .await; +} From 391fb226b596910aa8f2a5b8a4a537adb7907be3 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:47:41 +0300 Subject: [PATCH 09/64] Add test to CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0a0ce6..2364de84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,6 +157,7 @@ jobs: alice_and_bob_refund_using_cancel_and_refund_command, alice_and_bob_refund_using_cancel_then_refund_command, alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired, + alice_manually_punishes_after_bob_dead_and_bob_cancels punish, alice_punishes_after_restart_bob_dead, alice_manually_punishes_after_bob_dead, From 7e5c1ae45a3130d2684708abeef028e8ab1df8fa Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:09:18 +0300 Subject: [PATCH 10/64] fix formatting and typo --- swap/src/cli/cancel_and_refund.rs | 2 +- .../alice_and_bob_refund_using_cancel_and_refund_command.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index b1c0f2aa..9a152d0f 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -12,7 +12,7 @@ pub async fn cancel_and_refund( db: Arc, ) -> Result { match cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { - Ok((_, state @ BobState::BtcCancelled {..})) => { + Ok((_, state @ BobState::BtcCancelled { .. })) => { return Ok(state); } Err(err) => { diff --git a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command.rs b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command.rs index 1870bed4..04cabec4 100644 --- a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command.rs +++ b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command.rs @@ -50,7 +50,7 @@ async fn given_alice_and_bob_manually_refund_after_funds_locked_both_refund() { // Bob manually cancels bob_join_handle.abort(); - let (_, _, state) = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db).await?; + let (_, state) = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db).await?; assert!(matches!(state, BobState::BtcCancelled { .. })); let (bob_swap, bob_join_handle) = ctx From 957c33bffe53c62220719372b5bd737864aa837b Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:39:36 +0300 Subject: [PATCH 11/64] Fix stupid errors --- swap/src/cli/cancel_and_refund.rs | 15 ++++++++++----- ...lly_punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 9a152d0f..e71db5ef 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -12,7 +12,7 @@ pub async fn cancel_and_refund( db: Arc, ) -> Result { match cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { - Ok((_, state @ BobState::BtcCancelled { .. })) => { + Ok((_, state @ BobState::BtcPunished { .. })) => { return Ok(state); } Err(err) => { @@ -62,10 +62,12 @@ pub async fn cancel( match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await { Ok((txid, _)) => { - let state = BobState::BtcCancelled(state6); + let state = BobState::BtcPunished { + tx_lock_id: state6.tx_lock_id(), + }; db.insert_latest_state(swap_id, state.clone().into()) .await?; - return Ok((txid, state)); + Ok((txid, state)) } Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { @@ -73,7 +75,10 @@ pub async fn cancel( if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) || error_code == i64::from(RpcErrorCode::RpcVerifyError) { - let txid = state6.construct_tx_cancel().unwrap().txid(); + let txid = state6 + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; @@ -83,7 +88,7 @@ pub async fn cancel( } bail!(err); } - }; + } } pub async fn refund( diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index de0ba4e7..5eacefd1 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcCancelled { .. })); + assert!(matches!(state, BobState::BtcPunished { .. })); Ok(()) }) .await; From a9a1724ad32990a46f5a0a3350dd43136609c0cb Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:58:53 +0300 Subject: [PATCH 12/64] implement refund and lock check --- swap/src/cli/cancel_and_refund.rs | 67 ++++++++++++++++++------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index e71db5ef..c61bea45 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,4 @@ -use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; +use crate::bitcoin::{parse_rpc_error_code, ExpiredTimelocks, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; @@ -11,14 +11,8 @@ pub async fn cancel_and_refund( bitcoin_wallet: Arc, db: Arc, ) -> Result { - match cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { - Ok((_, state @ BobState::BtcPunished { .. })) => { - return Ok(state); - } - Err(err) => { - tracing::info!(%err, "Could not submit cancel transaction"); - } - _ => {} + if let Err(err) = cancel(swap_id, bitcoin_wallet.clone(), db.clone()).await { + tracing::info!(%err, "Could not submit cancel transaction"); }; let state = match refund(swap_id, bitcoin_wallet, db).await { @@ -62,35 +56,36 @@ pub async fn cancel( match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await { Ok((txid, _)) => { - let state = BobState::BtcPunished { - tx_lock_id: state6.tx_lock_id(), - }; + let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; Ok((txid, state)) } Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { - tracing::debug!(%error_code, "parse rpc error"); - if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) - || error_code == i64::from(RpcErrorCode::RpcVerifyError) - { - let txid = state6 - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); - let state = BobState::BtcCancelled(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice"); - return Ok((txid, state)); + if error_code == i64::from(RpcErrorCode::RpcVerifyError) { + if let ExpiredTimelocks::None { .. } = + state6.expired_timelock(bitcoin_wallet.as_ref()).await? + { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); + } else { + let txid = state6 + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); + return Ok((txid, state)); + } } } bail!(err); } } } - pub async fn refund( swap_id: Uuid, bitcoin_wallet: Arc, @@ -119,8 +114,26 @@ pub async fn refund( }; tracing::info!(%swap_id, "Manually refunding swap"); - state6.publish_refund_btc(bitcoin_wallet.as_ref()).await?; + match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { + Ok(_) => (), + Err(refund_error) => { + if refund_error + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") + { + let state = BobState::BtcPunished { + tx_lock_id: state6.tx_lock_id(), + }; + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + + tracing::info!("Cannot refund because BTC is punished by Alice."); + return Ok(state); + } + bail!(refund_error); + } + } let state = BobState::BtcRefunded(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; From e2b65381994360cda25087c29c209cada8a1ebfb Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 02:07:12 +0300 Subject: [PATCH 13/64] introduce better approach for checking for error --- swap/src/cli/cancel_and_refund.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index c61bea45..549dddbe 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,4 @@ -use crate::bitcoin::{parse_rpc_error_code, ExpiredTimelocks, RpcErrorCode, Wallet}; +use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; @@ -16,11 +16,12 @@ pub async fn cancel_and_refund( }; let state = match refund(swap_id, bitcoin_wallet, db).await { - Ok(s) => s, + Ok(s) => { + tracing::info!("Refund transaction submitted"); + s + } Err(e) => bail!(e), }; - - tracing::info!("Refund transaction submitted"); Ok(state) } @@ -64,12 +65,10 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = - state6.expired_timelock(bitcoin_wallet.as_ref()).await? + if err + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } else { let txid = state6 .construct_tx_cancel() .expect("Error when constructing tx_cancel") @@ -79,6 +78,9 @@ pub async fn cancel( .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); return Ok((txid, state)); + } else { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); } } } From c9e3c40987542305a78f65c8f98ea66c238deeda Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:24:35 +0300 Subject: [PATCH 14/64] Fix multiple things --- swap/src/cli/cancel_and_refund.rs | 65 ++++++++----------- swap/src/protocol/bob/state.rs | 56 ++++++++++++++-- ...punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 3 files changed, 78 insertions(+), 45 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 549dddbe..aab9f2cd 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,5 +1,5 @@ use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; -use crate::protocol::bob::BobState; +use crate::protocol::bob::{BobState, BtcCancelledByAlice, BtcPunishedWhileRefundError}; use crate::protocol::Database; use anyhow::{bail, Result}; use bitcoin::Txid; @@ -16,12 +16,11 @@ pub async fn cancel_and_refund( }; let state = match refund(swap_id, bitcoin_wallet, db).await { - Ok(s) => { - tracing::info!("Refund transaction submitted"); - s - } + Ok(s) => s, Err(e) => bail!(e), }; + + tracing::info!("Refund transaction submitted"); Ok(state) } @@ -65,24 +64,21 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if err - .to_string() - .contains("Failed to broadcast Bitcoin refund transaction") - { - let txid = state6 - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); - let state = BobState::BtcCancelled(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); - return Ok((txid, state)); - } else { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); + } else if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { + tracing::info!("Cancel transaction has already been confirmed on chain"); } + } else if let Some(error) = err.downcast_ref::() { + let txid = state6 + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!(%error); + return Ok((txid, state)); } bail!(err); } @@ -116,29 +112,24 @@ pub async fn refund( }; tracing::info!(%swap_id, "Manually refunding swap"); - match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { - Ok(_) => (), - Err(refund_error) => { - if refund_error - .to_string() - .contains("Failed to broadcast Bitcoin refund transaction") - { + Ok(()) => { + let state = BobState::BtcRefunded(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + Ok(state) + } + Err(error) => { + if let Some(error) = error.downcast_ref::() { + tracing::info!(%error); let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; db.insert_latest_state(swap_id, state.clone().into()) .await?; - - tracing::info!("Cannot refund because BTC is punished by Alice."); return Ok(state); } - bail!(refund_error); + bail!(error); } } - let state = BobState::BtcRefunded(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - - Ok(state) } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index a1fd750a..942d9de6 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -20,6 +20,20 @@ use sha2::Sha256; use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof; use std::fmt; use uuid::Uuid; +#[derive(Debug)] +pub struct BtcCancelledByAlice; +impl std::fmt::Display for BtcCancelledByAlice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice.") + } +} +#[derive(Debug)] +pub struct BtcPunishedWhileRefundError; +impl std::fmt::Display for BtcPunishedWhileRefundError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Cannot refund because BTC is punished.") + } +} #[derive(Debug, Clone, PartialEq, Serialize)] pub enum BobState { @@ -663,6 +677,7 @@ impl State6 { Ok(tx) } + pub fn construct_tx_cancel(&self) -> Result { bitcoin::TxCancel::new( &self.tx_lock, @@ -674,24 +689,51 @@ impl State6 { .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) .context("Failed to complete Bitcoin cancel transaction") } + pub fn print_type_of(&self, _: &T) { + tracing::debug!("{}", std::any::type_name::()) + } pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, ) -> Result<(Txid, Subscription)> { let transaction = self.construct_tx_cancel()?; - - let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; - - Ok((tx_id, subscription)) + match bitcoin_wallet.broadcast(transaction, "cancel").await { + Ok((txid, subscription)) => Ok((txid, subscription)), + Err(error) => { + match error.downcast_ref::() { + Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) + // UTXO is already spent, swap timeout. + if protocol_error.contains("bad-txns-inputs-missingorspent") => + { + return Err(anyhow!(BtcCancelledByAlice)); + } + _ => { + Err(error) + } + } + } + } } pub async fn publish_refund_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<()> { let signed_tx_refund = self.signed_refund_transaction()?; - bitcoin_wallet.broadcast(signed_tx_refund, "refund").await?; - Ok(()) + match bitcoin_wallet.broadcast(signed_tx_refund, "refund").await { + Ok((_, _)) => Ok(()), + Err(error) => { + match error.downcast_ref::() { + Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) + if protocol_error.contains("bad-txns-inputs-missingorspent") => + { + return Err(anyhow!(BtcPunishedWhileRefundError)); + } + _ => { + Err(error) + } + } + } + } } - pub fn signed_refund_transaction(&self) -> Result { let tx_cancel = bitcoin::TxCancel::new( &self.tx_lock, diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 5eacefd1..d880b60e 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcPunished { .. })); + assert!(matches!(state, BobState::BtcPunished { .. })); Ok(()) }) .await; From ca8a9cbc0bad84170781134defe09ffddf5ae935 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:03:00 +0300 Subject: [PATCH 15/64] Revert "Fix multiple things" This reverts commit d00293de3599a108a37a7f90ed8f60bf6e454a16. --- swap/src/cli/cancel_and_refund.rs | 65 +++++++++++-------- swap/src/protocol/bob/state.rs | 56 ++-------------- ...punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 3 files changed, 45 insertions(+), 78 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index aab9f2cd..549dddbe 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,5 +1,5 @@ use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; -use crate::protocol::bob::{BobState, BtcCancelledByAlice, BtcPunishedWhileRefundError}; +use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; use bitcoin::Txid; @@ -16,11 +16,12 @@ pub async fn cancel_and_refund( }; let state = match refund(swap_id, bitcoin_wallet, db).await { - Ok(s) => s, + Ok(s) => { + tracing::info!("Refund transaction submitted"); + s + } Err(e) => bail!(e), }; - - tracing::info!("Refund transaction submitted"); Ok(state) } @@ -64,21 +65,24 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } else if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { - tracing::info!("Cancel transaction has already been confirmed on chain"); + if err + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") + { + let txid = state6 + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); + return Ok((txid, state)); + } else { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); + } } - } else if let Some(error) = err.downcast_ref::() { - let txid = state6 - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); - let state = BobState::BtcCancelled(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - tracing::info!(%error); - return Ok((txid, state)); } bail!(err); } @@ -112,24 +116,29 @@ pub async fn refund( }; tracing::info!(%swap_id, "Manually refunding swap"); + match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { - Ok(()) => { - let state = BobState::BtcRefunded(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - Ok(state) - } - Err(error) => { - if let Some(error) = error.downcast_ref::() { - tracing::info!(%error); + Ok(_) => (), + Err(refund_error) => { + if refund_error + .to_string() + .contains("Failed to broadcast Bitcoin refund transaction") + { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; db.insert_latest_state(swap_id, state.clone().into()) .await?; + + tracing::info!("Cannot refund because BTC is punished by Alice."); return Ok(state); } - bail!(error); + bail!(refund_error); } } + let state = BobState::BtcRefunded(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + + Ok(state) } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 942d9de6..a1fd750a 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -20,20 +20,6 @@ use sha2::Sha256; use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof; use std::fmt; use uuid::Uuid; -#[derive(Debug)] -pub struct BtcCancelledByAlice; -impl std::fmt::Display for BtcCancelledByAlice { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice.") - } -} -#[derive(Debug)] -pub struct BtcPunishedWhileRefundError; -impl std::fmt::Display for BtcPunishedWhileRefundError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Cannot refund because BTC is punished.") - } -} #[derive(Debug, Clone, PartialEq, Serialize)] pub enum BobState { @@ -677,7 +663,6 @@ impl State6 { Ok(tx) } - pub fn construct_tx_cancel(&self) -> Result { bitcoin::TxCancel::new( &self.tx_lock, @@ -689,51 +674,24 @@ impl State6 { .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) .context("Failed to complete Bitcoin cancel transaction") } - pub fn print_type_of(&self, _: &T) { - tracing::debug!("{}", std::any::type_name::()) - } pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, ) -> Result<(Txid, Subscription)> { let transaction = self.construct_tx_cancel()?; - match bitcoin_wallet.broadcast(transaction, "cancel").await { - Ok((txid, subscription)) => Ok((txid, subscription)), - Err(error) => { - match error.downcast_ref::() { - Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) - // UTXO is already spent, swap timeout. - if protocol_error.contains("bad-txns-inputs-missingorspent") => - { - return Err(anyhow!(BtcCancelledByAlice)); - } - _ => { - Err(error) - } - } - } - } + + let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; + + Ok((tx_id, subscription)) } pub async fn publish_refund_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<()> { let signed_tx_refund = self.signed_refund_transaction()?; + bitcoin_wallet.broadcast(signed_tx_refund, "refund").await?; - match bitcoin_wallet.broadcast(signed_tx_refund, "refund").await { - Ok((_, _)) => Ok(()), - Err(error) => { - match error.downcast_ref::() { - Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(serde_json::Value::String(ref protocol_error)))) - if protocol_error.contains("bad-txns-inputs-missingorspent") => - { - return Err(anyhow!(BtcPunishedWhileRefundError)); - } - _ => { - Err(error) - } - } - } - } + Ok(()) } + pub fn signed_refund_transaction(&self) -> Result { let tx_cancel = bitcoin::TxCancel::new( &self.tx_lock, diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index d880b60e..5eacefd1 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcPunished { .. })); + assert!(matches!(state, BobState::BtcPunished { .. })); Ok(()) }) .await; From 4421db7aad8758dfa7021e5f68f6e25a0f2ea565 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:04:05 +0300 Subject: [PATCH 16/64] Revert "introduce better approach for checking for error" This reverts commit 7844ad1483afe8830ef7119071b7307551592ae2. --- swap/src/cli/cancel_and_refund.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 549dddbe..c61bea45 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,4 @@ -use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; +use crate::bitcoin::{parse_rpc_error_code, ExpiredTimelocks, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; @@ -16,12 +16,11 @@ pub async fn cancel_and_refund( }; let state = match refund(swap_id, bitcoin_wallet, db).await { - Ok(s) => { - tracing::info!("Refund transaction submitted"); - s - } + Ok(s) => s, Err(e) => bail!(e), }; + + tracing::info!("Refund transaction submitted"); Ok(state) } @@ -65,10 +64,12 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if err - .to_string() - .contains("Failed to broadcast Bitcoin refund transaction") + if let ExpiredTimelocks::None { .. } = + state6.expired_timelock(bitcoin_wallet.as_ref()).await? { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); + } else { let txid = state6 .construct_tx_cancel() .expect("Error when constructing tx_cancel") @@ -78,9 +79,6 @@ pub async fn cancel( .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); return Ok((txid, state)); - } else { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); } } } From 4635d0aae9660a9968112deaa8ffd1400d380b08 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:13:21 +0300 Subject: [PATCH 17/64] use original approach and add timelock check --- swap/src/cli/cancel_and_refund.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index c61bea45..f370b9c0 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -117,21 +117,25 @@ pub async fn refund( match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { Ok(_) => (), - Err(refund_error) => { - if refund_error - .to_string() - .contains("Failed to broadcast Bitcoin refund transaction") - { - let state = BobState::BtcPunished { - tx_lock_id: state6.tx_lock_id(), - }; - db.insert_latest_state(swap_id, state.clone().into()) - .await?; + Err(error) => { + if let Ok(error_code) = parse_rpc_error_code(&error) { + if error_code == i64::from(RpcErrorCode::RpcVerifyError) { + if let ExpiredTimelocks::None { .. } = + state6.expired_timelock(bitcoin_wallet.as_ref()).await? + { + bail!(error); + } else { + let state = BobState::BtcPunished { + tx_lock_id: state6.tx_lock_id(), + }; + db.insert_latest_state(swap_id, state.clone().into()) + .await?; - tracing::info!("Cannot refund because BTC is punished by Alice."); - return Ok(state); + tracing::info!("Cannot refund because BTC is punished by Alice."); + return Ok(state); + } + } } - bail!(refund_error); } } let state = BobState::BtcRefunded(state6); From 789f25be3fc857aa847361f5033ce9871e96e432 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:25:18 +0300 Subject: [PATCH 18/64] Add missing comma --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2364de84..b3f742fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,7 +157,7 @@ jobs: alice_and_bob_refund_using_cancel_and_refund_command, alice_and_bob_refund_using_cancel_then_refund_command, alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired, - alice_manually_punishes_after_bob_dead_and_bob_cancels + alice_manually_punishes_after_bob_dead_and_bob_cancels, punish, alice_punishes_after_restart_bob_dead, alice_manually_punishes_after_bob_dead, From 1c6e757f02488c8e90387f721d1bd3f79ef28e93 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:34:20 +0300 Subject: [PATCH 19/64] Use assert_bob_punished --- .../alice_manually_punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 5eacefd1..84b1ef7f 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcPunished { .. })); + ctx.assert_bob_punished(state).await; Ok(()) }) .await; From 8b9f1b04c97f9cfad027490cc67464c9ff7ace5e Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:46:48 +0300 Subject: [PATCH 20/64] refactor and print successful refund message only if it succeeded --- swap/src/cli/cancel_and_refund.rs | 5 +++-- ...alice_manually_punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index f370b9c0..9ac26d97 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -19,8 +19,9 @@ pub async fn cancel_and_refund( Ok(s) => s, Err(e) => bail!(e), }; - - tracing::info!("Refund transaction submitted"); + if matches!(state, BobState::BtcRefunded { .. }) { + tracing::info!("Refund transaction submitted"); + } Ok(state) } diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 84b1ef7f..739df338 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -73,7 +73,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let (bob_swap, _) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; - + assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; ctx.assert_bob_punished(state).await; From 70220d15812141a52e85944cf305c55f7e238ce1 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:07:43 +0300 Subject: [PATCH 21/64] Add comments about logic --- swap/src/cli/cancel_and_refund.rs | 19 ++++++++++--------- swap/src/protocol/bob/state.rs | 2 +- ...punishes_after_bob_dead_and_bob_cancels.rs | 6 +++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 9ac26d97..630fb9db 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -19,7 +19,7 @@ pub async fn cancel_and_refund( Ok(s) => s, Err(e) => bail!(e), }; - if matches!(state, BobState::BtcRefunded { .. }) { + if matches!(state, BobState::BtcRefunded { .. }) { //Print success message only if refund succeeded. tracing::info!("Refund transaction submitted"); } Ok(state) @@ -65,17 +65,17 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = - state6.expired_timelock(bitcoin_wallet.as_ref()).await? + if let ExpiredTimelocks::None { .. } = // Check if timelock haven't expired. + state6.expired_timelock(bitcoin_wallet.as_ref()).await? { tracing::debug!(%error_code, "parse rpc error"); tracing::info!("General error trying to submit cancel transaction"); - } else { + } else { // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) let txid = state6 - .construct_tx_cancel() + .construct_tx_cancel() // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. .expect("Error when constructing tx_cancel") .txid(); - let state = BobState::BtcCancelled(state6); + let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. db.insert_latest_state(swap_id, state.clone().into()) .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); @@ -121,14 +121,14 @@ pub async fn refund( Err(error) => { if let Ok(error_code) = parse_rpc_error_code(&error) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = + if let ExpiredTimelocks::None { .. } = // Check if timelock haven't expired. state6.expired_timelock(bitcoin_wallet.as_ref()).await? { bail!(error); - } else { + } else { // Timelock expired and network rejected refund, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), - }; + }; // Set state to punished. db.insert_latest_state(swap_id, state.clone().into()) .await?; @@ -137,6 +137,7 @@ pub async fn refund( } } } + bail!(error); } } let state = BobState::BtcRefunded(state6); diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index a1fd750a..20c177fe 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -663,7 +663,7 @@ impl State6 { Ok(tx) } - pub fn construct_tx_cancel(&self) -> Result { + pub fn construct_tx_cancel(&self) -> Result { // Just construct the tx_cancel without broadcasting. bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 739df338..f1e8ad1d 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -73,10 +73,10 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let (bob_swap, _) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; - assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); + assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is out of sync with Alice. let state = - cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - ctx.assert_bob_punished(state).await; + cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; // Bob tries to cancel and refund. + ctx.assert_bob_punished(state).await; // Bob should be in punished state now. (This PR adds that behaviour.) Ok(()) }) .await; From 25ebcf731dac589f79ccd70674674c6e44b56472 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:12:02 +0300 Subject: [PATCH 22/64] fix formatting --- swap/src/cli/cancel_and_refund.rs | 19 ++++++++++++------- swap/src/protocol/bob/state.rs | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 630fb9db..be7a6100 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -19,7 +19,8 @@ pub async fn cancel_and_refund( Ok(s) => s, Err(e) => bail!(e), }; - if matches!(state, BobState::BtcRefunded { .. }) { //Print success message only if refund succeeded. + if matches!(state, BobState::BtcRefunded { .. }) { + //Print success message only if refund succeeded. tracing::info!("Refund transaction submitted"); } Ok(state) @@ -65,14 +66,16 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = // Check if timelock haven't expired. - state6.expired_timelock(bitcoin_wallet.as_ref()).await? + if let ExpiredTimelocks::None { .. } = // Check if timelock hasn't expired. + state6.expired_timelock(bitcoin_wallet.as_ref()).await? { tracing::debug!(%error_code, "parse rpc error"); tracing::info!("General error trying to submit cancel transaction"); - } else { // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) + } else { + // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) let txid = state6 - .construct_tx_cancel() // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. + // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. + .construct_tx_cancel() .expect("Error when constructing tx_cancel") .txid(); let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. @@ -121,11 +124,13 @@ pub async fn refund( Err(error) => { if let Ok(error_code) = parse_rpc_error_code(&error) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = // Check if timelock haven't expired. + // Check if timelock hasn't expired. + if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { bail!(error); - } else { // Timelock expired and network rejected refund, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) + // Timelock expired and network rejected refund, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) + } else { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; // Set state to punished. diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 20c177fe..042257f8 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -663,7 +663,8 @@ impl State6 { Ok(tx) } - pub fn construct_tx_cancel(&self) -> Result { // Just construct the tx_cancel without broadcasting. + pub fn construct_tx_cancel(&self) -> Result { + // Just construct the tx_cancel without broadcasting. bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, From aabff429bce260f9edd5f04a9787ef0c30ab86e1 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:17:45 +0300 Subject: [PATCH 23/64] fix formatting: delete whitespaces --- swap/src/cli/cancel_and_refund.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index be7a6100..6a596985 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -124,13 +124,13 @@ pub async fn refund( Err(error) => { if let Ok(error_code) = parse_rpc_error_code(&error) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - // Check if timelock hasn't expired. + // Check if timelock hasn't expired. if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { bail!(error); // Timelock expired and network rejected refund, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) - } else { + } else { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; // Set state to punished. From 25ff29d633712343ffacfef56a2ee2bb72e8db0c Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:30:12 +0300 Subject: [PATCH 24/64] Refactor and describe 'subscription' deletion --- swap/src/cli/cancel_and_refund.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 6a596985..a1457095 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -31,6 +31,7 @@ pub async fn cancel( bitcoin_wallet: Arc, db: Arc, ) -> Result<(Txid, BobState)> { + // Subscription was introduced in commit f39e1de964f7e2f81f9624043834a31154b68a55, but isn't used anywhere. We can't get transaction if Alice already cancelled the swap. let state = db.get_state(swap_id).await?.try_into()?; let state6 = match state { @@ -66,12 +67,9 @@ pub async fn cancel( Err(err) => { if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::None { .. } = // Check if timelock hasn't expired. + if let ExpiredTimelocks::Punish { .. } = // Check if timelock is punished. state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } else { // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) let txid = state6 // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. @@ -83,6 +81,9 @@ pub async fn cancel( .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); return Ok((txid, state)); + } else { + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); } } } @@ -124,13 +125,10 @@ pub async fn refund( Err(error) => { if let Ok(error_code) = parse_rpc_error_code(&error) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - // Check if timelock hasn't expired. - if let ExpiredTimelocks::None { .. } = + // Check if timelock is punished. + if let ExpiredTimelocks::Punish { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - bail!(error); - // Timelock expired and network rejected refund, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) - } else { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; // Set state to punished. From dc84437f63c88b591202eb6da8bf974e6a78deaf Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:31:21 +0300 Subject: [PATCH 25/64] better description --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index a1457095..ccdf4df9 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -131,7 +131,7 @@ pub async fn refund( { let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), - }; // Set state to punished. + }; // Set state to punished, because timelock expired and in punished state. db.insert_latest_state(swap_id, state.clone().into()) .await?; From 7f80966b86116b56f6ab2b3ba73f1dacecb8502d Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:33:21 +0300 Subject: [PATCH 26/64] better description --- swap/src/cli/cancel_and_refund.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index ccdf4df9..65ee457b 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -70,7 +70,7 @@ pub async fn cancel( if let ExpiredTimelocks::Punish { .. } = // Check if timelock is punished. state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (I assume that there's no other states under these conditions, am I right?) + // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (Assuming that there's no other possible states under these conditions) let txid = state6 // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. .construct_tx_cancel() @@ -125,13 +125,14 @@ pub async fn refund( Err(error) => { if let Ok(error_code) = parse_rpc_error_code(&error) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - // Check if timelock is punished. + // Check if timelock expired. if let ExpiredTimelocks::Punish { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { + // Timelock expired and network rejected refund, so we are out-of-sync with Alice and punished. (Assuming that there's no other possible states under these conditions) let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), - }; // Set state to punished, because timelock expired and in punished state. + }; db.insert_latest_state(swap_id, state.clone().into()) .await?; From 5dc717d3e8186bbd3423dc2d7c55ef4f2563182d Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:38:38 +0300 Subject: [PATCH 27/64] formalize description --- .../alice_manually_punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index f1e8ad1d..c7344883 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -76,7 +76,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is out of sync with Alice. let state = cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; // Bob tries to cancel and refund. - ctx.assert_bob_punished(state).await; // Bob should be in punished state now. (This PR adds that behaviour.) + ctx.assert_bob_punished(state).await; // Bob should be in punished state now. (PR #1668 adds that behaviour.) Ok(()) }) .await; From 8ac04e0d16c1b68737553cbcb84466a1695871ca Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:45:38 +0300 Subject: [PATCH 28/64] Check if Alice already published tx_cancel/tx_punish if we get an error when trying to cancel/refund. Test: use cli::cancel and cli::refund to check if cli::cancel sets state to BtcCancelled --- swap/src/bitcoin/punish.rs | 4 +- swap/src/cli/cancel_and_refund.rs | 84 +++++++++---------- swap/src/protocol/bob/state.rs | 38 +++++++++ ...punishes_after_bob_dead_and_bob_cancels.rs | 15 +++- 4 files changed, 95 insertions(+), 46 deletions(-) diff --git a/swap/src/bitcoin/punish.rs b/swap/src/bitcoin/punish.rs index 9d687544..a2280dba 100644 --- a/swap/src/bitcoin/punish.rs +++ b/swap/src/bitcoin/punish.rs @@ -44,7 +44,9 @@ impl TxPunish { watch_script: punish_address.script_pubkey(), } } - + pub fn txid(&self) -> Txid { + self.inner.txid() + } pub fn digest(&self) -> Sighash { self.digest } diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 65ee457b..d5844f78 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,4 @@ -use crate::bitcoin::{parse_rpc_error_code, ExpiredTimelocks, RpcErrorCode, Wallet}; +use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; @@ -65,26 +65,27 @@ pub async fn cancel( Ok((txid, state)) } Err(err) => { + if state6 + .check_for_tx_cancel(bitcoin_wallet.as_ref()) + .await + .is_ok() + { + // Alice already cancelled, so we are out-of-sync with Alice. + let txid = state6 + // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. + .construct_tx_cancel() + .expect("Error when constructing tx_cancel") + .txid(); + let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); + return Ok((txid, state)); + } if let Ok(error_code) = parse_rpc_error_code(&err) { if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - if let ExpiredTimelocks::Punish { .. } = // Check if timelock is punished. - state6.expired_timelock(bitcoin_wallet.as_ref()).await? - { - // Timelock expired and network rejected tx_cancel, so we are out-of-sync with Alice. (Assuming that there's no other possible states under these conditions) - let txid = state6 - // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); - let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); - return Ok((txid, state)); - } else { - tracing::debug!(%error_code, "parse rpc error"); - tracing::info!("General error trying to submit cancel transaction"); - } + tracing::debug!(%error_code, "parse rpc error"); + tracing::info!("General error trying to submit cancel transaction"); } } bail!(err); @@ -121,32 +122,31 @@ pub async fn refund( tracing::info!(%swap_id, "Manually refunding swap"); match state6.publish_refund_btc(bitcoin_wallet.as_ref()).await { - Ok(_) => (), - Err(error) => { - if let Ok(error_code) = parse_rpc_error_code(&error) { - if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - // Check if timelock expired. - if let ExpiredTimelocks::Punish { .. } = - state6.expired_timelock(bitcoin_wallet.as_ref()).await? - { - // Timelock expired and network rejected refund, so we are out-of-sync with Alice and punished. (Assuming that there's no other possible states under these conditions) - let state = BobState::BtcPunished { - tx_lock_id: state6.tx_lock_id(), - }; - db.insert_latest_state(swap_id, state.clone().into()) - .await?; + Ok(_) => { + let state = BobState::BtcRefunded(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; - tracing::info!("Cannot refund because BTC is punished by Alice."); - return Ok(state); - } - } + Ok(state) + } + Err(error) => { + if state6 + .check_for_tx_punish(bitcoin_wallet.as_ref()) + .await + .is_ok() + { + // Alice already punished us, so we are out-of-sync with Alice and should set our state to the BtcPunished. + tracing::debug!("we are punished"); + let state = BobState::BtcPunished { + tx_lock_id: state6.tx_lock_id(), + }; + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + + tracing::info!("Cannot refund because BTC is punished by Alice."); + return Ok(state); } bail!(error); } } - let state = BobState::BtcRefunded(state6); - db.insert_latest_state(swap_id, state.clone().into()) - .await?; - - Ok(state) } diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 042257f8..cea537e0 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -347,6 +347,7 @@ impl State2 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, + punish_address: self.punish_address, tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -354,6 +355,7 @@ impl State2 { tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, + tx_punish_fee: self.tx_punish_fee, }, self.tx_lock, )) @@ -373,6 +375,7 @@ pub struct State3 { punish_timelock: PunishTimelock, refund_address: bitcoin::Address, redeem_address: bitcoin::Address, + punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -383,6 +386,8 @@ pub struct State3 { tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_cancel_fee: bitcoin::Amount, + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + tx_punish_fee: bitcoin::Amount, } impl State3 { @@ -411,6 +416,7 @@ impl State3 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, + punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -418,6 +424,7 @@ impl State3 { tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, + tx_punish_fee: self.tx_punish_fee, } } @@ -429,11 +436,13 @@ impl State3 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address.clone(), + punish_address: self.punish_address.clone(), tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a.clone(), tx_refund_encsig: self.tx_refund_encsig.clone(), tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, + tx_punish_fee: self.tx_punish_fee, } } @@ -476,6 +485,7 @@ pub struct State4 { punish_timelock: PunishTimelock, refund_address: bitcoin::Address, redeem_address: bitcoin::Address, + punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -486,6 +496,8 @@ pub struct State4 { tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_cancel_fee: bitcoin::Amount, + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + tx_punish_fee: bitcoin::Amount, } impl State4 { @@ -574,11 +586,13 @@ impl State4 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, + punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, + tx_punish_fee: self.tx_punish_fee, } } } @@ -614,6 +628,7 @@ pub struct State6 { cancel_timelock: CancelTimelock, punish_timelock: PunishTimelock, refund_address: bitcoin::Address, + punish_address: bitcoin::Address, tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -621,6 +636,8 @@ pub struct State6 { pub tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_cancel_fee: bitcoin::Amount, + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + pub tx_punish_fee: bitcoin::Amount, } impl State6 { @@ -663,6 +680,27 @@ impl State6 { Ok(tx) } + // Check if tx_punish already exists. + pub async fn check_for_tx_punish( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result { + let tx_cancel = bitcoin::TxCancel::new( + &self.tx_lock, + self.cancel_timelock, + self.A, + self.b.public(), + self.tx_cancel_fee, + )?; + let tx_punish = bitcoin::TxPunish::new( + &tx_cancel, + &self.punish_address, + self.punish_timelock, + self.tx_punish_fee, + ); + let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; + Ok(tx) + } pub fn construct_tx_cancel(&self) -> Result { // Just construct the tx_cancel without broadcasting. bitcoin::TxCancel::new( diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index c7344883..d57dd783 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -70,12 +70,21 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { asb::punish(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await?; ctx.assert_alice_punished(alice_state).await; - let (bob_swap, _) = ctx + let (bob_swap, bob_join_handle) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is out of sync with Alice. - let state = - cli::cancel_and_refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; // Bob tries to cancel and refund. + bob_join_handle.abort(); + + let (_, state) = cli::cancel(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; + assert!(matches!(state, BobState::BtcCancelled { .. })); + + let (bob_swap, _) = ctx + .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) + .await; + assert!(matches!(bob_swap.state, BobState::BtcCancelled { .. })); + + let state = cli::refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; // Bob tries to refund. ctx.assert_bob_punished(state).await; // Bob should be in punished state now. (PR #1668 adds that behaviour.) Ok(()) }) From 1d2223c54df97aa0618078ff26bd80782f688910 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:48:37 +0300 Subject: [PATCH 29/64] Return accidentally deleted check for RpcVerifyAlreadyInChain --- swap/src/cli/cancel_and_refund.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index d5844f78..7c990a60 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -83,8 +83,9 @@ pub async fn cancel( return Ok((txid, state)); } if let Ok(error_code) = parse_rpc_error_code(&err) { - if error_code == i64::from(RpcErrorCode::RpcVerifyError) { - tracing::debug!(%error_code, "parse rpc error"); + if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { + tracing::info!("Cancel transaction has already been confirmed on chain"); + } else if error_code == i64::from(RpcErrorCode::RpcVerifyError) { tracing::info!("General error trying to submit cancel transaction"); } } From 5f392e3b7ba139eec1821cf4c134217c1541f984 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:50:53 +0300 Subject: [PATCH 30/64] return accidentally removed code --- swap/src/cli/cancel_and_refund.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 7c990a60..f4db3712 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -83,6 +83,7 @@ pub async fn cancel( return Ok((txid, state)); } if let Ok(error_code) = parse_rpc_error_code(&err) { + tracing::debug!(%error_code, "parse rpc error"); if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { tracing::info!("Cancel transaction has already been confirmed on chain"); } else if error_code == i64::from(RpcErrorCode::RpcVerifyError) { From 6c2c205dd8a949eb51a758e4996a9ee049230bc7 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:55:32 +0300 Subject: [PATCH 31/64] Remove redundant debug print --- swap/src/cli/cancel_and_refund.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index f4db3712..f21a765f 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -138,7 +138,6 @@ pub async fn refund( .is_ok() { // Alice already punished us, so we are out-of-sync with Alice and should set our state to the BtcPunished. - tracing::debug!("we are punished"); let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; From 4b444140c03a90e9b58a5235620396f7fca58613 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:21:14 +0300 Subject: [PATCH 32/64] Remove redundant comment --- swap/src/protocol/bob/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index cea537e0..d5152d35 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -680,7 +680,7 @@ impl State6 { Ok(tx) } - // Check if tx_punish already exists. + pub async fn check_for_tx_punish( &self, bitcoin_wallet: &bitcoin::Wallet, From 8866e60323173190f70e572366dd626703301d7f Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:37:55 +0300 Subject: [PATCH 33/64] Use transaction created by check_for_tx_cancel and delete construct_tx_cancel --- swap/src/cli/cancel_and_refund.rs | 13 ++----------- swap/src/protocol/bob/state.rs | 17 +++++++---------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index f21a765f..b6095664 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -65,22 +65,13 @@ pub async fn cancel( Ok((txid, state)) } Err(err) => { - if state6 - .check_for_tx_cancel(bitcoin_wallet.as_ref()) - .await - .is_ok() - { + if let Ok(tx) = state6.check_for_tx_cancel(bitcoin_wallet.as_ref()).await { // Alice already cancelled, so we are out-of-sync with Alice. - let txid = state6 - // Construct tx_cancel without broadcasting to the network, because swap has already been cancelled by Alice. - .construct_tx_cancel() - .expect("Error when constructing tx_cancel") - .txid(); let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. db.insert_latest_state(swap_id, state.clone().into()) .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); - return Ok((txid, state)); + return Ok((tx.txid(), state)); } if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index d5152d35..38871dd0 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -701,9 +701,12 @@ impl State6 { let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; Ok(tx) } - pub fn construct_tx_cancel(&self) -> Result { - // Just construct the tx_cancel without broadcasting. - bitcoin::TxCancel::new( + + pub async fn submit_tx_cancel( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result<(Txid, Subscription)> { + let transaction = bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, self.A, @@ -711,13 +714,7 @@ impl State6 { self.tx_cancel_fee, )? .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) - .context("Failed to complete Bitcoin cancel transaction") - } - pub async fn submit_tx_cancel( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result<(Txid, Subscription)> { - let transaction = self.construct_tx_cancel()?; + .context("Failed to complete Bitcoin cancel transaction")?; let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; From 756f817daa960ba86b106e79d0bdd511bbaaad69 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sat, 8 Jun 2024 20:33:59 +0300 Subject: [PATCH 34/64] Reimplement tx_punish check without breaking database changes --- swap/src/cli/cancel_and_refund.rs | 16 +++++++++-- swap/src/protocol/bob/state.rs | 44 ++++++++++++++----------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index b6095664..6dbff4cb 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,6 +1,6 @@ use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; -use crate::protocol::Database; +use crate::protocol::{Database, State}; use anyhow::{bail, Result}; use bitcoin::Txid; use std::sync::Arc; @@ -123,7 +123,19 @@ pub async fn refund( Ok(state) } Err(error) => { - if state6 + let state3 = db + .get_states(swap_id) + .await? + .iter() + .find_map(|state| { + if let State::Bob(BobState::BtcLocked { state3, .. }) = state { + Some(state3.clone()) + } else { + None + } + }); + if state3 + .expect("Error: weren't able to extract State3 from database.") .check_for_tx_punish(bitcoin_wallet.as_ref()) .await .is_ok() diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 38871dd0..31417893 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -436,7 +436,6 @@ impl State3 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address.clone(), - punish_address: self.punish_address.clone(), tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a.clone(), tx_refund_encsig: self.tx_refund_encsig.clone(), @@ -472,6 +471,26 @@ impl State3 { tx_cancel_status, )) } + pub async fn check_for_tx_punish( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result { + let tx_cancel = bitcoin::TxCancel::new( + &self.tx_lock, + self.cancel_timelock, + self.A, + self.b.public(), + self.tx_cancel_fee, + )?; + let tx_punish = bitcoin::TxPunish::new( + &tx_cancel, + &self.punish_address, + self.punish_timelock, + self.tx_punish_fee, + ); + let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; + Ok(tx) + } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] @@ -586,7 +605,6 @@ impl State4 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, - punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -628,7 +646,6 @@ pub struct State6 { cancel_timelock: CancelTimelock, punish_timelock: PunishTimelock, refund_address: bitcoin::Address, - punish_address: bitcoin::Address, tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -681,27 +698,6 @@ impl State6 { Ok(tx) } - pub async fn check_for_tx_punish( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result { - let tx_cancel = bitcoin::TxCancel::new( - &self.tx_lock, - self.cancel_timelock, - self.A, - self.b.public(), - self.tx_cancel_fee, - )?; - let tx_punish = bitcoin::TxPunish::new( - &tx_cancel, - &self.punish_address, - self.punish_timelock, - self.tx_punish_fee, - ); - let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; - Ok(tx) - } - pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, From a1333212552f8ae0a642064069f92fa9720a0cb1 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sat, 8 Jun 2024 20:36:07 +0300 Subject: [PATCH 35/64] fix formatting --- swap/src/cli/cancel_and_refund.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 6dbff4cb..2974c5ed 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -123,16 +123,12 @@ pub async fn refund( Ok(state) } Err(error) => { - let state3 = db - .get_states(swap_id) - .await? - .iter() - .find_map(|state| { + let state3 = db.get_states(swap_id).await?.iter().find_map(|state| { if let State::Bob(BobState::BtcLocked { state3, .. }) = state { Some(state3.clone()) - } else { - None - } + } else { + None + } }); if state3 .expect("Error: weren't able to extract State3 from database.") From 1f9ac6416646910f6242613147d48d60c7cf25d9 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:32:06 +0300 Subject: [PATCH 36/64] Revert "fix formatting" This reverts commit 990206596b3696d4c1fc20730614bdc4d7c82601. --- swap/src/cli/cancel_and_refund.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 2974c5ed..6dbff4cb 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -123,12 +123,16 @@ pub async fn refund( Ok(state) } Err(error) => { - let state3 = db.get_states(swap_id).await?.iter().find_map(|state| { + let state3 = db + .get_states(swap_id) + .await? + .iter() + .find_map(|state| { if let State::Bob(BobState::BtcLocked { state3, .. }) = state { Some(state3.clone()) - } else { - None - } + } else { + None + } }); if state3 .expect("Error: weren't able to extract State3 from database.") From 2c37e6e4f40c5ed6f8504ce35234dee3971a317d Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:32:41 +0300 Subject: [PATCH 37/64] Revert "Reimplement tx_punish check without breaking database changes" This reverts commit ab33bcde8fccb6b2af97cbb9d45ec82a0500b3a1. --- swap/src/cli/cancel_and_refund.rs | 16 ++--------- swap/src/protocol/bob/state.rs | 44 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 6dbff4cb..b6095664 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,6 +1,6 @@ use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; -use crate::protocol::{Database, State}; +use crate::protocol::Database; use anyhow::{bail, Result}; use bitcoin::Txid; use std::sync::Arc; @@ -123,19 +123,7 @@ pub async fn refund( Ok(state) } Err(error) => { - let state3 = db - .get_states(swap_id) - .await? - .iter() - .find_map(|state| { - if let State::Bob(BobState::BtcLocked { state3, .. }) = state { - Some(state3.clone()) - } else { - None - } - }); - if state3 - .expect("Error: weren't able to extract State3 from database.") + if state6 .check_for_tx_punish(bitcoin_wallet.as_ref()) .await .is_ok() diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 31417893..38871dd0 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -436,6 +436,7 @@ impl State3 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address.clone(), + punish_address: self.punish_address.clone(), tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a.clone(), tx_refund_encsig: self.tx_refund_encsig.clone(), @@ -471,26 +472,6 @@ impl State3 { tx_cancel_status, )) } - pub async fn check_for_tx_punish( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result { - let tx_cancel = bitcoin::TxCancel::new( - &self.tx_lock, - self.cancel_timelock, - self.A, - self.b.public(), - self.tx_cancel_fee, - )?; - let tx_punish = bitcoin::TxPunish::new( - &tx_cancel, - &self.punish_address, - self.punish_timelock, - self.tx_punish_fee, - ); - let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; - Ok(tx) - } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] @@ -605,6 +586,7 @@ impl State4 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, + punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -646,6 +628,7 @@ pub struct State6 { cancel_timelock: CancelTimelock, punish_timelock: PunishTimelock, refund_address: bitcoin::Address, + punish_address: bitcoin::Address, tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -698,6 +681,27 @@ impl State6 { Ok(tx) } + pub async fn check_for_tx_punish( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result { + let tx_cancel = bitcoin::TxCancel::new( + &self.tx_lock, + self.cancel_timelock, + self.A, + self.b.public(), + self.tx_cancel_fee, + )?; + let tx_punish = bitcoin::TxPunish::new( + &tx_cancel, + &self.punish_address, + self.punish_timelock, + self.tx_punish_fee, + ); + let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; + Ok(tx) + } + pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, From 08a97ba1b7b098ed2d0b2025418a4b765d13f7dc Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 09:22:49 +0300 Subject: [PATCH 38/64] provide better comments --- swap/src/cli/cancel_and_refund.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index b6095664..ced5fb45 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -66,8 +66,8 @@ pub async fn cancel( } Err(err) => { if let Ok(tx) = state6.check_for_tx_cancel(bitcoin_wallet.as_ref()).await { - // Alice already cancelled, so we are out-of-sync with Alice. - let state = BobState::BtcCancelled(state6); // Set state to cancelled to sync with Alice. + // Alice already cancelled swap, so Bob sets his state to BtcCancelled. + let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); @@ -128,7 +128,7 @@ pub async fn refund( .await .is_ok() { - // Alice already punished us, so we are out-of-sync with Alice and should set our state to the BtcPunished. + // Alice already punished Bob, so Bob sets his state to BtcPunished. let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; From f66c16a3817fdb2e1fe2613fe85f168afebc17dc Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 09:25:08 +0300 Subject: [PATCH 39/64] provide better comments --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index ced5fb45..686fb7fb 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -31,7 +31,7 @@ pub async fn cancel( bitcoin_wallet: Arc, db: Arc, ) -> Result<(Txid, BobState)> { - // Subscription was introduced in commit f39e1de964f7e2f81f9624043834a31154b68a55, but isn't used anywhere. We can't get transaction if Alice already cancelled the swap. + // Subscription was introduced in commit f39e1de964f7e2f81f9624043834a31154b68a55, but isn't used anywhere. In case tx_cancel already exists, the function can't return a subscription from submit_tx_cancel because Alice has already broadcast tx_cancel. let state = db.get_state(swap_id).await?.try_into()?; let state6 = match state { From f2973bf5cc8dcb1ad8ecf4a8b683bb7a590f43cb Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:16:16 +0300 Subject: [PATCH 40/64] remove missleading comment --- swap/src/cli/cancel_and_refund.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 686fb7fb..1b2d5455 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -31,7 +31,6 @@ pub async fn cancel( bitcoin_wallet: Arc, db: Arc, ) -> Result<(Txid, BobState)> { - // Subscription was introduced in commit f39e1de964f7e2f81f9624043834a31154b68a55, but isn't used anywhere. In case tx_cancel already exists, the function can't return a subscription from submit_tx_cancel because Alice has already broadcast tx_cancel. let state = db.get_state(swap_id).await?.try_into()?; let state6 = match state { From 0caf678192117b4c2d7c666a836ef5c99dca553d Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:44:52 +0300 Subject: [PATCH 41/64] better comments --- ...ce_manually_punishes_after_bob_dead_and_bob_cancels.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index d57dd783..8885b1c5 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -73,19 +73,19 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let (bob_swap, bob_join_handle) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; - assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is out of sync with Alice. + assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is in wrong state. bob_join_handle.abort(); let (_, state) = cli::cancel(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcCancelled { .. })); + assert!(matches!(state, BobState::BtcCancelled { .. })); // Bob should be in BtcCancelled state now. let (bob_swap, _) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; assert!(matches!(bob_swap.state, BobState::BtcCancelled { .. })); - let state = cli::refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; // Bob tries to refund. - ctx.assert_bob_punished(state).await; // Bob should be in punished state now. (PR #1668 adds that behaviour.) + let state = cli::refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; + ctx.assert_bob_punished(state).await; // Bob should be in BtcPunished state now. Ok(()) }) .await; From f5f8af92a014e8993cf22a640154414af92fe6eb Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:19:11 +0300 Subject: [PATCH 42/64] check for punish timelock in fn refund, not yet expired timelock in cancel and remove breaking database changes --- swap/src/cli/cancel_and_refund.rs | 15 ++++++----- swap/src/protocol/bob/state.rs | 43 ------------------------------- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 1b2d5455..6730bec0 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -1,4 +1,4 @@ -use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet}; +use crate::bitcoin::{parse_rpc_error_code, ExpiredTimelocks, RpcErrorCode, Wallet}; use crate::protocol::bob::BobState; use crate::protocol::Database; use anyhow::{bail, Result}; @@ -72,6 +72,11 @@ pub async fn cancel( tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); return Ok((tx.txid(), state)); } + if let ExpiredTimelocks::None { .. } = + state6.expired_timelock(bitcoin_wallet.as_ref()).await? + { + tracing::info!("Timelock hasn't expired yet."); + } if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { @@ -122,10 +127,8 @@ pub async fn refund( Ok(state) } Err(error) => { - if state6 - .check_for_tx_punish(bitcoin_wallet.as_ref()) - .await - .is_ok() + if let ExpiredTimelocks::Punish { .. } = + state6.expired_timelock(bitcoin_wallet.as_ref()).await? { // Alice already punished Bob, so Bob sets his state to BtcPunished. let state = BobState::BtcPunished { @@ -134,7 +137,7 @@ pub async fn refund( db.insert_latest_state(swap_id, state.clone().into()) .await?; - tracing::info!("Cannot refund because BTC is punished by Alice."); + tracing::info!("Cannot refund because BTC is punished."); return Ok(state); } bail!(error); diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 38871dd0..515be0fb 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -267,14 +267,12 @@ impl State1 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, - punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: msg.tx_cancel_sig, tx_refund_encsig: msg.tx_refund_encsig, min_monero_confirmations: self.min_monero_confirmations, tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, - tx_punish_fee: self.tx_punish_fee, tx_cancel_fee: self.tx_cancel_fee, }) } @@ -293,7 +291,6 @@ pub struct State2 { pub punish_timelock: PunishTimelock, pub refund_address: bitcoin::Address, redeem_address: bitcoin::Address, - punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -301,8 +298,6 @@ pub struct State2 { #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_redeem_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - tx_punish_fee: bitcoin::Amount, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_cancel_fee: bitcoin::Amount, @@ -347,7 +342,6 @@ impl State2 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, - punish_address: self.punish_address, tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -355,7 +349,6 @@ impl State2 { tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, - tx_punish_fee: self.tx_punish_fee, }, self.tx_lock, )) @@ -375,7 +368,6 @@ pub struct State3 { punish_timelock: PunishTimelock, refund_address: bitcoin::Address, redeem_address: bitcoin::Address, - punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -386,8 +378,6 @@ pub struct State3 { tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_cancel_fee: bitcoin::Amount, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - tx_punish_fee: bitcoin::Amount, } impl State3 { @@ -416,7 +406,6 @@ impl State3 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, - punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, @@ -424,7 +413,6 @@ impl State3 { tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, - tx_punish_fee: self.tx_punish_fee, } } @@ -436,13 +424,11 @@ impl State3 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address.clone(), - punish_address: self.punish_address.clone(), tx_lock: self.tx_lock.clone(), tx_cancel_sig_a: self.tx_cancel_sig_a.clone(), tx_refund_encsig: self.tx_refund_encsig.clone(), tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, - tx_punish_fee: self.tx_punish_fee, } } @@ -485,7 +471,6 @@ pub struct State4 { punish_timelock: PunishTimelock, refund_address: bitcoin::Address, redeem_address: bitcoin::Address, - punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -496,8 +481,6 @@ pub struct State4 { tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_cancel_fee: bitcoin::Amount, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - tx_punish_fee: bitcoin::Amount, } impl State4 { @@ -586,13 +569,11 @@ impl State4 { cancel_timelock: self.cancel_timelock, punish_timelock: self.punish_timelock, refund_address: self.refund_address, - punish_address: self.punish_address, tx_lock: self.tx_lock, tx_cancel_sig_a: self.tx_cancel_sig_a, tx_refund_encsig: self.tx_refund_encsig, tx_refund_fee: self.tx_refund_fee, tx_cancel_fee: self.tx_cancel_fee, - tx_punish_fee: self.tx_punish_fee, } } } @@ -628,7 +609,6 @@ pub struct State6 { cancel_timelock: CancelTimelock, punish_timelock: PunishTimelock, refund_address: bitcoin::Address, - punish_address: bitcoin::Address, tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -636,8 +616,6 @@ pub struct State6 { pub tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_cancel_fee: bitcoin::Amount, - #[serde(with = "::bitcoin::util::amount::serde::as_sat")] - pub tx_punish_fee: bitcoin::Amount, } impl State6 { @@ -681,27 +659,6 @@ impl State6 { Ok(tx) } - pub async fn check_for_tx_punish( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result { - let tx_cancel = bitcoin::TxCancel::new( - &self.tx_lock, - self.cancel_timelock, - self.A, - self.b.public(), - self.tx_cancel_fee, - )?; - let tx_punish = bitcoin::TxPunish::new( - &tx_cancel, - &self.punish_address, - self.punish_timelock, - self.tx_punish_fee, - ); - let tx = bitcoin_wallet.get_raw_transaction(tx_punish.txid()).await?; - Ok(tx) - } - pub async fn submit_tx_cancel( &self, bitcoin_wallet: &bitcoin::Wallet, From 0d40179a65dd5b757b2cf42d114ff45cde5e78bc Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:24:06 +0300 Subject: [PATCH 43/64] remove changes from state.rs --- swap/src/protocol/bob/state.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 515be0fb..dea2c7d8 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -268,11 +268,13 @@ impl State1 { refund_address: self.refund_address, redeem_address: self.redeem_address, tx_lock: self.tx_lock, + punish_address: self.punish_address, tx_cancel_sig_a: msg.tx_cancel_sig, tx_refund_encsig: msg.tx_refund_encsig, min_monero_confirmations: self.min_monero_confirmations, tx_redeem_fee: self.tx_redeem_fee, tx_refund_fee: self.tx_refund_fee, + tx_punish_fee: self.tx_punish_fee, tx_cancel_fee: self.tx_cancel_fee, }) } @@ -291,6 +293,7 @@ pub struct State2 { pub punish_timelock: PunishTimelock, pub refund_address: bitcoin::Address, redeem_address: bitcoin::Address, + punish_address: bitcoin::Address, pub tx_lock: bitcoin::TxLock, tx_cancel_sig_a: Signature, tx_refund_encsig: bitcoin::EncryptedSignature, @@ -298,6 +301,8 @@ pub struct State2 { #[serde(with = "::bitcoin::util::amount::serde::as_sat")] tx_redeem_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] + tx_punish_fee: bitcoin::Amount, + #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_refund_fee: bitcoin::Amount, #[serde(with = "::bitcoin::util::amount::serde::as_sat")] pub tx_cancel_fee: bitcoin::Amount, From d84edabcd06d673afe1ed494f683a354b98b891f Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:24:42 +0300 Subject: [PATCH 44/64] remove changes from state.rs --- swap/src/protocol/bob/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index dea2c7d8..58c1d723 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -267,8 +267,8 @@ impl State1 { punish_timelock: self.punish_timelock, refund_address: self.refund_address, redeem_address: self.redeem_address, - tx_lock: self.tx_lock, punish_address: self.punish_address, + tx_lock: self.tx_lock, tx_cancel_sig_a: msg.tx_cancel_sig, tx_refund_encsig: msg.tx_refund_encsig, min_monero_confirmations: self.min_monero_confirmations, From b57678b0368b75ec0f864e8eb51cff855e8a97f7 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:29:57 +0300 Subject: [PATCH 45/64] remove changes to punish.rs --- swap/src/bitcoin/punish.rs | 4 +--- swap/src/cli/cancel_and_refund.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/swap/src/bitcoin/punish.rs b/swap/src/bitcoin/punish.rs index a2280dba..9d687544 100644 --- a/swap/src/bitcoin/punish.rs +++ b/swap/src/bitcoin/punish.rs @@ -44,9 +44,7 @@ impl TxPunish { watch_script: punish_address.script_pubkey(), } } - pub fn txid(&self) -> Txid { - self.inner.txid() - } + pub fn digest(&self) -> Sighash { self.digest } diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 6730bec0..3b4a9011 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -130,7 +130,7 @@ pub async fn refund( if let ExpiredTimelocks::Punish { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - // Alice already punished Bob, so Bob sets his state to BtcPunished. + // Punish timelock already expired, so we can't refund. let state = BobState::BtcPunished { tx_lock_id: state6.tx_lock_id(), }; From 92757ac91987215ea6d527c66c78ec04d0dff2af Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:31:52 +0300 Subject: [PATCH 46/64] more fitting log message --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 3b4a9011..0a7b5ce9 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -69,7 +69,7 @@ pub async fn cancel( let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; - tracing::info!("Cancel transaction has already been confirmed on chain. The swap has therefore already been cancelled by Alice."); + tracing::info!("Cancel transaction has already been confirmed on chain."); return Ok((tx.txid(), state)); } if let ExpiredTimelocks::None { .. } = From 60b548282b38cce09ab5cf998b17e238b341859a Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:39:56 +0300 Subject: [PATCH 47/64] beautify log message --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 0a7b5ce9..48ebe9b4 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -75,7 +75,7 @@ pub async fn cancel( if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - tracing::info!("Timelock hasn't expired yet."); + tracing::info!("Cancel timelock hasn't expired yet."); } if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); From 64fb090eeadd8d84ed8d4141eb2e1425b973ecaf Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:45:01 +0300 Subject: [PATCH 48/64] beautify comments --- ...anually_punishes_after_bob_dead_and_bob_cancels.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 8885b1c5..31a7cf07 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -69,23 +69,24 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let (_, alice_state) = asb::punish(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await?; ctx.assert_alice_punished(alice_state).await; - + // Bob is in wrong state. let (bob_swap, bob_join_handle) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; - assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); // Bob is in wrong state. + assert!(matches!(bob_swap.state, BobState::BtcLocked { .. })); bob_join_handle.abort(); let (_, state) = cli::cancel(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - assert!(matches!(state, BobState::BtcCancelled { .. })); // Bob should be in BtcCancelled state now. + // Bob should be in BtcCancelled state now. + assert!(matches!(state, BobState::BtcCancelled { .. })); let (bob_swap, _) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; assert!(matches!(bob_swap.state, BobState::BtcCancelled { .. })); - + // Alice punished Bob, so he should be in the BtcPunished state. let state = cli::refund(bob_swap_id, bob_swap.bitcoin_wallet, bob_swap.db).await?; - ctx.assert_bob_punished(state).await; // Bob should be in BtcPunished state now. + ctx.assert_bob_punished(state).await; Ok(()) }) .await; From 6f1a4e7fbeeb970a934f05d6d4f5f1a4a298e665 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:48:52 +0300 Subject: [PATCH 49/64] better log messages --- swap/src/cli/cancel_and_refund.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 48ebe9b4..1b55317d 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -69,7 +69,7 @@ pub async fn cancel( let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; - tracing::info!("Cancel transaction has already been confirmed on chain."); + tracing::info!("Cancel transaction has already been confirmed on chain"); return Ok((tx.txid(), state)); } if let ExpiredTimelocks::None { .. } = @@ -137,7 +137,7 @@ pub async fn refund( db.insert_latest_state(swap_id, state.clone().into()) .await?; - tracing::info!("Cannot refund because BTC is punished."); + tracing::info!("Cannot refund because BTC punish timelock expired"); return Ok(state); } bail!(error); From b80cc820f703b4b8d4b826ed075a5d5d1d3a38ec Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:53:06 +0300 Subject: [PATCH 50/64] fix formatting --- .../alice_manually_punishes_after_bob_dead_and_bob_cancels.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs index 31a7cf07..131f04f1 100644 --- a/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs +++ b/swap/tests/alice_manually_punishes_after_bob_dead_and_bob_cancels.rs @@ -69,7 +69,7 @@ async fn alice_manually_punishes_after_bob_dead_and_bob_cancels() { let (_, alice_state) = asb::punish(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await?; ctx.assert_alice_punished(alice_state).await; - // Bob is in wrong state. + // Bob is in wrong state. let (bob_swap, bob_join_handle) = ctx .stop_and_resume_bob_from_db(bob_join_handle, bob_swap_id) .await; From 3beb41c25c190cda8b4a01876f56a7d6c6a5cca7 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:21:47 +0300 Subject: [PATCH 51/64] Throw appealing error in case that cancel timelock hasnt expired yet --- swap/src/cli/cancel_and_refund.rs | 2 +- ...d_using_cancel_and_refund_command_timelock_not_expired.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 1b55317d..7b773e1e 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -75,7 +75,7 @@ pub async fn cancel( if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - tracing::info!("Cancel timelock hasn't expired yet."); + bail!("Cancel timelock hasn't expired yet"); } if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); diff --git a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs index 16953866..11eb2f0b 100644 --- a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs +++ b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs @@ -42,10 +42,7 @@ async fn given_alice_and_bob_manually_cancel_when_timelock_not_expired_errors() let error = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db) .await .unwrap_err(); - assert_eq!( - parse_rpc_error_code(&error).unwrap(), - i64::from(RpcErrorCode::RpcVerifyRejected) - ); + assert_eq!(error.to_string(), "Cancel timelock hasn't expired yet"); ctx.restart_alice().await; let alice_swap = ctx.alice_next_swap().await; From af5dad585abb51ad4044a1471599d30b8f78f432 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 22:59:24 +0300 Subject: [PATCH 52/64] Better error messages --- swap/src/cli/cancel_and_refund.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 7b773e1e..092c4979 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -71,21 +71,24 @@ pub async fn cancel( .await?; tracing::info!("Cancel transaction has already been confirmed on chain"); return Ok((tx.txid(), state)); - } - if let ExpiredTimelocks::None { .. } = + } else if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { - bail!("Cancel timelock hasn't expired yet"); - } - if let Ok(error_code) = parse_rpc_error_code(&err) { + bail!("Cancel timelock hasn't expired yet. Bob tried to cancel the swap too early"); + } else if let Ok(error_code) = parse_rpc_error_code(&err) { tracing::debug!(%error_code, "parse rpc error"); if error_code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) { tracing::info!("Cancel transaction has already been confirmed on chain"); } else if error_code == i64::from(RpcErrorCode::RpcVerifyError) { tracing::info!("General error trying to submit cancel transaction"); } + let state = BobState::BtcCancelled(state6); + db.insert_latest_state(swap_id, state.clone().into()) + .await?; + Ok((txid, state)) + } else { + bail!("Error while trying to submit cancel transaction, this shouldn't have happened. {}", err); } - bail!(err); } } } From 075db8164e6aefdb1f1d23b2b5c645064b1c6813 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:08:01 +0300 Subject: [PATCH 53/64] fix error --- swap/src/cli/cancel_and_refund.rs | 1 + swap/src/protocol/bob/state.rs | 22 +++++++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 092c4979..12588ca6 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -83,6 +83,7 @@ pub async fn cancel( tracing::info!("General error trying to submit cancel transaction"); } let state = BobState::BtcCancelled(state6); + let txid = state6.construct_tx_cancel().txid(); db.insert_latest_state(swap_id, state.clone().into()) .await?; Ok((txid, state)) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 58c1d723..9c15626d 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -646,18 +646,20 @@ impl State6 { tx_cancel_status, )) } - - pub async fn check_for_tx_cancel( - &self, - bitcoin_wallet: &bitcoin::Wallet, - ) -> Result { - let tx_cancel = bitcoin::TxCancel::new( + pub async fn construct_tx_cancel(&self) -> Result { + bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, self.A, self.b.public(), self.tx_cancel_fee, )?; + } + pub async fn check_for_tx_cancel( + &self, + bitcoin_wallet: &bitcoin::Wallet, + ) -> Result { + let tx_cancel = self.construct_tx_cancel()?; let tx = bitcoin_wallet.get_raw_transaction(tx_cancel.txid()).await?; @@ -668,13 +670,7 @@ impl State6 { &self, bitcoin_wallet: &bitcoin::Wallet, ) -> Result<(Txid, Subscription)> { - let transaction = bitcoin::TxCancel::new( - &self.tx_lock, - self.cancel_timelock, - self.A, - self.b.public(), - self.tx_cancel_fee, - )? + let transaction = self.construct_tx_cancel()? .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) .context("Failed to complete Bitcoin cancel transaction")?; From 3b575a37b83f7c71dac586597ffaa156b5b07cbf Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:10:37 +0300 Subject: [PATCH 54/64] fix error --- swap/src/protocol/bob/state.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 9c15626d..5dff9848 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -653,7 +653,7 @@ impl State6 { self.A, self.b.public(), self.tx_cancel_fee, - )?; + ) } pub async fn check_for_tx_cancel( &self, @@ -687,13 +687,7 @@ impl State6 { } pub fn signed_refund_transaction(&self) -> Result { - let tx_cancel = bitcoin::TxCancel::new( - &self.tx_lock, - self.cancel_timelock, - self.A, - self.b.public(), - self.tx_cancel_fee, - )?; + let tx_cancel = self.construct_tx_cancel()?; let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address, self.tx_refund_fee); From a2f7de24ac8210a3abe2e40793097e12dafdf88b Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:10:51 +0300 Subject: [PATCH 55/64] fix error --- swap/src/protocol/bob/state.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 5dff9848..30cd3e70 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -670,9 +670,10 @@ impl State6 { &self, bitcoin_wallet: &bitcoin::Wallet, ) -> Result<(Txid, Subscription)> { - let transaction = self.construct_tx_cancel()? - .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) - .context("Failed to complete Bitcoin cancel transaction")?; + let transaction = self + .construct_tx_cancel()? + .complete_as_bob(self.A, self.b.clone(), self.tx_cancel_sig_a.clone()) + .context("Failed to complete Bitcoin cancel transaction")?; let (tx_id, subscription) = bitcoin_wallet.broadcast(transaction, "cancel").await?; From e8ed4250f26a5e9ca44cfdf598599ccb985b261a Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:12:29 +0300 Subject: [PATCH 56/64] fix typo --- swap/src/protocol/bob/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/protocol/bob/state.rs b/swap/src/protocol/bob/state.rs index 30cd3e70..03f56d28 100644 --- a/swap/src/protocol/bob/state.rs +++ b/swap/src/protocol/bob/state.rs @@ -646,7 +646,7 @@ impl State6 { tx_cancel_status, )) } - pub async fn construct_tx_cancel(&self) -> Result { + pub fn construct_tx_cancel(&self) -> Result { bitcoin::TxCancel::new( &self.tx_lock, self.cancel_timelock, From 8cf2f1f22bec0b3f52bb4bcd2d2444225c70c971 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:13:59 +0300 Subject: [PATCH 57/64] fix typo --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 12588ca6..a00f721a 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -83,7 +83,7 @@ pub async fn cancel( tracing::info!("General error trying to submit cancel transaction"); } let state = BobState::BtcCancelled(state6); - let txid = state6.construct_tx_cancel().txid(); + let txid = state6.construct_tx_cancel()?.txid(); db.insert_latest_state(swap_id, state.clone().into()) .await?; Ok((txid, state)) From 3b53d36b0bed1560d33b4740988df815f7e0b617 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:15:20 +0300 Subject: [PATCH 58/64] fix typo --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index a00f721a..5a888494 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -82,8 +82,8 @@ pub async fn cancel( } else if error_code == i64::from(RpcErrorCode::RpcVerifyError) { tracing::info!("General error trying to submit cancel transaction"); } - let state = BobState::BtcCancelled(state6); let txid = state6.construct_tx_cancel()?.txid(); + let state = BobState::BtcCancelled(state6); db.insert_latest_state(swap_id, state.clone().into()) .await?; Ok((txid, state)) From 02e7625bf61ab3bac0e94c52c8009ade10e5c9a7 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:19:49 +0300 Subject: [PATCH 59/64] fix test --- ...fund_using_cancel_and_refund_command_timelock_not_expired.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs index 11eb2f0b..0c388f85 100644 --- a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs +++ b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs @@ -42,7 +42,7 @@ async fn given_alice_and_bob_manually_cancel_when_timelock_not_expired_errors() let error = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db) .await .unwrap_err(); - assert_eq!(error.to_string(), "Cancel timelock hasn't expired yet"); + assert_eq!(error.to_string(), "Cancel timelock hasn't expired yet. Bob tried to cancel the swap too early"); ctx.restart_alice().await; let alice_swap = ctx.alice_next_swap().await; From bbe1ad0c57f09799e46665af6b18597f1b87b75e Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:21:12 +0300 Subject: [PATCH 60/64] fix formatting --- ...d_using_cancel_and_refund_command_timelock_not_expired.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs index 0c388f85..00f56b5c 100644 --- a/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs +++ b/swap/tests/alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired.rs @@ -42,7 +42,10 @@ async fn given_alice_and_bob_manually_cancel_when_timelock_not_expired_errors() let error = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db) .await .unwrap_err(); - assert_eq!(error.to_string(), "Cancel timelock hasn't expired yet. Bob tried to cancel the swap too early"); + assert_eq!( + error.to_string(), + "Cancel timelock hasn't expired yet. Bob tried to cancel the swap too early" + ); ctx.restart_alice().await; let alice_swap = ctx.alice_next_swap().await; From d415959097604d20d254484ca85b52e801ccef27 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:23:17 +0300 Subject: [PATCH 61/64] fix lint --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 5a888494..b84c75e4 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -70,7 +70,7 @@ pub async fn cancel( db.insert_latest_state(swap_id, state.clone().into()) .await?; tracing::info!("Cancel transaction has already been confirmed on chain"); - return Ok((tx.txid(), state)); + Ok((tx.txid(), state)); } else if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { From a1a0a79154e29dcb291c41bc2f55793839af392d Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Wed, 12 Jun 2024 23:25:30 +0300 Subject: [PATCH 62/64] fix typo --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index b84c75e4..5ea660d8 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -70,7 +70,7 @@ pub async fn cancel( db.insert_latest_state(swap_id, state.clone().into()) .await?; tracing::info!("Cancel transaction has already been confirmed on chain"); - Ok((tx.txid(), state)); + Ok((tx.txid(), state)) } else if let ExpiredTimelocks::None { .. } = state6.expired_timelock(bitcoin_wallet.as_ref()).await? { From cf50f9050099f18f411c012ed74a12c43f2442a8 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Thu, 13 Jun 2024 20:54:22 +0300 Subject: [PATCH 63/64] remove dot --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 5ea660d8..9d629695 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -88,7 +88,7 @@ pub async fn cancel( .await?; Ok((txid, state)) } else { - bail!("Error while trying to submit cancel transaction, this shouldn't have happened. {}", err); + bail!("Error while trying to submit cancel transaction, this shouldn't have happened {}", err); } } } From bb7b4e15633fea8d7c3400fabc268e5f174ca1e3 Mon Sep 17 00:00:00 2001 From: patrini32 <171664803+patrini32@users.noreply.github.com> Date: Sat, 15 Jun 2024 15:45:57 +0300 Subject: [PATCH 64/64] better log message --- swap/src/cli/cancel_and_refund.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swap/src/cli/cancel_and_refund.rs b/swap/src/cli/cancel_and_refund.rs index 9d629695..8a300871 100644 --- a/swap/src/cli/cancel_and_refund.rs +++ b/swap/src/cli/cancel_and_refund.rs @@ -88,7 +88,7 @@ pub async fn cancel( .await?; Ok((txid, state)) } else { - bail!("Error while trying to submit cancel transaction, this shouldn't have happened {}", err); + bail!("Unhandled error while trying to submit cancel transaction, this shouldn't have happened {}", err); } } }