Skip to content

Commit

Permalink
feat(swap): merge cancel/refund commands into one command
Browse files Browse the repository at this point in the history
  • Loading branch information
delta1 committed Jan 8, 2023
1 parent 4e7ba95 commit f39e1de
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 177 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Expand Up @@ -134,6 +134,7 @@ jobs:
happy_path_restart_bob_before_xmr_locked,
happy_path_restart_alice_after_xmr_locked,
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,
punish,
alice_punishes_after_restart_bob_dead,
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Swap: merge separate cancel/refund commands into one `cancel-and-refund` command for stuck swaps

## [0.12.0] - 2022-12-31

### Changed
Expand Down
1 change: 1 addition & 0 deletions bors.toml
Expand Up @@ -13,6 +13,7 @@ status = [
"docker_tests (happy_path_restart_alice_after_xmr_locked)",
"docker_tests (happy_path_restart_bob_before_xmr_locked)",
"docker_tests (alice_and_bob_refund_using_cancel_and_refund_command)",
"docker_tests (alice_and_bob_refund_using_cancel_then_refund_command)",
"docker_tests (alice_and_bob_refund_using_cancel_and_refund_command_timelock_not_expired)",
"docker_tests (punish)",
"docker_tests (alice_punishes_after_restart_bob_dead)",
Expand Down
29 changes: 4 additions & 25 deletions swap/src/bin/swap.rs
Expand Up @@ -324,7 +324,7 @@ async fn main() -> Result<()> {
}
}
}
Command::Cancel {
Command::CancelAndRefund {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
Expand All @@ -344,30 +344,7 @@ async fn main() -> Result<()> {
)
.await?;

let (txid, _) = cli::cancel(swap_id, Arc::new(bitcoin_wallet), db).await?;
tracing::debug!("Cancel transaction successfully published with id {}", txid);
}
Command::Refund {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
} => {
cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?;

let db = open_db(data_dir.join("sqlite")).await?;
let seed = Seed::from_file_or_generate(data_dir.as_path())
.context("Failed to read in seed file")?;

let bitcoin_wallet = init_bitcoin_wallet(
bitcoin_electrum_rpc_url,
&seed,
data_dir,
env_config,
bitcoin_target_block,
)
.await?;

cli::refund(swap_id, Arc::new(bitcoin_wallet), db).await?;
cli::cancel_and_refund(swap_id, Arc::new(bitcoin_wallet), db).await?;
}
Command::ListSellers {
rendezvous_point,
Expand Down Expand Up @@ -523,6 +500,7 @@ async fn init_bitcoin_wallet(
env_config: Config,
bitcoin_target_block: usize,
) -> Result<bitcoin::Wallet> {
tracing::debug!("Initializing bitcoin wallet");
let xprivkey = seed.derive_extended_private_key(env_config.bitcoin_network)?;

let wallet = bitcoin::Wallet::new(
Expand All @@ -535,6 +513,7 @@ async fn init_bitcoin_wallet(
.await
.context("Failed to initialize Bitcoin wallet")?;

tracing::debug!("Syncing bitcoin wallet");
wallet.sync().await?;

Ok(wallet)
Expand Down
6 changes: 2 additions & 4 deletions swap/src/cli.rs
@@ -1,17 +1,15 @@
mod behaviour;
pub mod cancel;
pub mod cancel_and_refund;
pub mod command;
mod event_loop;
mod list_sellers;
pub mod refund;
pub mod tracing;
pub mod transport;

pub use behaviour::{Behaviour, OutEvent};
pub use cancel::cancel;
pub use cancel_and_refund::{cancel, cancel_and_refund, refund};
pub use event_loop::{EventLoop, EventLoopHandle};
pub use list_sellers::{list_sellers, Seller, Status as SellerStatus};
pub use refund::refund;

#[cfg(test)]
mod tests {
Expand Down
56 changes: 0 additions & 56 deletions swap/src/cli/cancel.rs

This file was deleted.

115 changes: 115 additions & 0 deletions swap/src/cli/cancel_and_refund.rs
@@ -0,0 +1,115 @@
use crate::bitcoin::wallet::Subscription;
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Wallet};
use crate::protocol::bob::BobState;
use crate::protocol::Database;
use anyhow::{bail, Result};
use bitcoin::Txid;
use std::sync::Arc;
use uuid::Uuid;

pub async fn cancel_and_refund(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
) -> Result<BobState> {
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 {
Ok(s) => s,
Err(e) => bail!(e),
};

tracing::info!("Refund transaction submitted");
Ok(state)
}

pub async fn cancel(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
) -> Result<(Txid, Subscription, BobState)> {
let state = db.get_state(swap_id).await?.try_into()?;

let state6 = match state {
BobState::BtcLocked { state3, .. } => state3.cancel(),
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
BobState::XmrLocked(state4) => state4.cancel(),
BobState::EncSigSent(state4) => state4.cancel(),
BobState::CancelTimelockExpired(state6) => state6,
BobState::BtcRefunded(state6) => state6,
BobState::BtcCancelled(state6) => state6,

BobState::Started { .. }
| BobState::SwapSetupCompleted(_)
| BobState::BtcRedeemed(_)
| BobState::XmrRedeemed { .. }
| BobState::BtcPunished { .. }
| BobState::SafelyAborted => bail!(
"Cannot cancel swap {} because it is in state {} which is not refundable.",
swap_id,
state
),
};

tracing::info!(%swap_id, "Manually cancelling swap");

let (txid, subscription) = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
Ok(txid) => txid,
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");
}
}
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(
swap_id: Uuid,
bitcoin_wallet: Arc<Wallet>,
db: Arc<dyn Database>,
) -> Result<BobState> {
let state = db.get_state(swap_id).await?.try_into()?;

let state6 = match state {
BobState::BtcLocked { state3, .. } => state3.cancel(),
BobState::XmrLockProofReceived { state, .. } => state.cancel(),
BobState::XmrLocked(state4) => state4.cancel(),
BobState::EncSigSent(state4) => state4.cancel(),
BobState::CancelTimelockExpired(state6) => state6,
BobState::BtcCancelled(state6) => state6,
BobState::Started { .. }
| BobState::SwapSetupCompleted(_)
| BobState::BtcRedeemed(_)
| BobState::BtcRefunded(_)
| BobState::XmrRedeemed { .. }
| BobState::BtcPunished { .. }
| BobState::SafelyAborted => bail!(
"Cannot refund swap {} because it is in state {} which is not refundable.",
swap_id,
state
),
};

tracing::info!(%swap_id, "Manually refunding swap");
state6.publish_refund_btc(bitcoin_wallet.as_ref()).await?;

let state = BobState::BtcRefunded(state6);
db.insert_latest_state(swap_id, state.clone().into())
.await?;

Ok(state)
}
53 changes: 10 additions & 43 deletions swap/src/cli/command.rs
Expand Up @@ -184,7 +184,7 @@ where
},
}
}
RawCommand::Cancel {
RawCommand::CancelAndRefund {
swap_id: SwapId { swap_id },
bitcoin,
} => {
Expand All @@ -196,26 +196,7 @@ where
debug,
json,
data_dir: data::data_dir_from(data, is_testnet)?,
cmd: Command::Cancel {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
},
}
}
RawCommand::Refund {
swap_id: SwapId { swap_id },
bitcoin,
} => {
let (bitcoin_electrum_rpc_url, bitcoin_target_block) =
bitcoin.apply_defaults(is_testnet)?;

Arguments {
env_config: env_config_from(is_testnet),
debug,
json,
data_dir: data::data_dir_from(data, is_testnet)?,
cmd: Command::Refund {
cmd: Command::CancelAndRefund {
swap_id,
bitcoin_electrum_rpc_url,
bitcoin_target_block,
Expand Down Expand Up @@ -297,12 +278,7 @@ pub enum Command {
tor_socks5_port: u16,
namespace: XmrBtcNamespace,
},
Cancel {
swap_id: Uuid,
bitcoin_electrum_rpc_url: Url,
bitcoin_target_block: usize,
},
Refund {
CancelAndRefund {
swap_id: Uuid,
bitcoin_electrum_rpc_url: Url,
bitcoin_target_block: usize,
Expand Down Expand Up @@ -422,18 +398,9 @@ enum RawCommand {
#[structopt(flatten)]
tor: Tor,
},
/// Force submission of the cancel transaction overriding the protocol state
/// machine and blockheight checks (expert users only)
Cancel {
#[structopt(flatten)]
swap_id: SwapId,

#[structopt(flatten)]
bitcoin: Bitcoin,
},
/// Force submission of the refund transaction overriding the protocol state
/// machine and blockheight checks (expert users only)
Refund {
/// Force the submission of the cancel and refund transactions of a swap
#[structopt(aliases = &["cancel", "refund"])]
CancelAndRefund {
#[structopt(flatten)]
swap_id: SwapId,

Expand Down Expand Up @@ -1275,7 +1242,7 @@ mod tests {
debug: false,
json: false,
data_dir: data_dir_path_cli().join(TESTNET),
cmd: Command::Cancel {
cmd: Command::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
.unwrap(),
Expand All @@ -1290,7 +1257,7 @@ mod tests {
debug: false,
json: false,
data_dir: data_dir_path_cli().join(MAINNET),
cmd: Command::Cancel {
cmd: Command::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
Expand All @@ -1304,7 +1271,7 @@ mod tests {
debug: false,
json: false,
data_dir: data_dir_path_cli().join(TESTNET),
cmd: Command::Refund {
cmd: Command::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL_TESTNET)
.unwrap(),
Expand All @@ -1319,7 +1286,7 @@ mod tests {
debug: false,
json: false,
data_dir: data_dir_path_cli().join(MAINNET),
cmd: Command::Refund {
cmd: Command::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
bitcoin_electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
bitcoin_target_block: DEFAULT_BITCOIN_CONFIRMATION_TARGET,
Expand Down

0 comments on commit f39e1de

Please sign in to comment.