mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-16 10:11:11 -04:00
feat(swap): merge cancel/refund commands into one command
This commit is contained in:
parent
4e7ba9517b
commit
f39e1de964
12 changed files with 220 additions and 177 deletions
|
@ -1,56 +0,0 @@
|
|||
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Txid, Wallet};
|
||||
use crate::protocol::bob::BobState;
|
||||
use crate::protocol::Database;
|
||||
use anyhow::{bail, Result};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn cancel(
|
||||
swap_id: Uuid,
|
||||
bitcoin_wallet: Arc<Wallet>,
|
||||
db: Arc<dyn Database>,
|
||||
) -> Result<(Txid, 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 = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
||||
Ok(txid) => txid,
|
||||
Err(err) => {
|
||||
if let Ok(code) = parse_rpc_error_code(&err) {
|
||||
if code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) {
|
||||
tracing::info!("Cancel transaction has already been confirmed on chain")
|
||||
}
|
||||
}
|
||||
bail!(err);
|
||||
}
|
||||
};
|
||||
|
||||
let state = BobState::BtcCancelled(state6);
|
||||
db.insert_latest_state(swap_id, state.clone().into())
|
||||
.await?;
|
||||
|
||||
Ok((txid, state))
|
||||
}
|
115
swap/src/cli/cancel_and_refund.rs
Normal file
115
swap/src/cli/cancel_and_refund.rs
Normal file
|
@ -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)
|
||||
}
|
|
@ -184,7 +184,7 @@ where
|
|||
},
|
||||
}
|
||||
}
|
||||
RawCommand::Cancel {
|
||||
RawCommand::CancelAndRefund {
|
||||
swap_id: SwapId { swap_id },
|
||||
bitcoin,
|
||||
} => {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
use crate::bitcoin::Wallet;
|
||||
use crate::protocol::bob::BobState;
|
||||
use crate::protocol::Database;
|
||||
use anyhow::{bail, Result};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
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
|
||||
),
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue