mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Inform user if cancel tx is has already been published
Alice and Bob can both submit cancel. A scenario exists where one of them may try and manually cancel but the other party has already published cancel. Log a message to notify the user this has happened. Add reusable function to check error for bitcoin rpc error code
This commit is contained in:
parent
f511ff093c
commit
110a5d2229
@ -1,4 +1,4 @@
|
|||||||
use crate::bitcoin::{Txid, Wallet};
|
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Txid, Wallet};
|
||||||
use crate::database::{Database, Swap};
|
use crate::database::{Database, Swap};
|
||||||
use crate::protocol::alice::AliceState;
|
use crate::protocol::alice::AliceState;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
@ -44,9 +44,11 @@ pub async fn cancel(
|
|||||||
let txid = match state3.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
let txid = match state3.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
||||||
Ok(txid) => txid,
|
Ok(txid) => txid,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(bdk::Error::TransactionConfirmed) = err.downcast_ref::<bdk::Error>() {
|
if let Ok(code) = parse_rpc_error_code(&err) {
|
||||||
tracing::info!("Cancel transaction has already been published and confirmed")
|
if code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) {
|
||||||
};
|
tracing::info!("Cancel transaction has already been confirmed on chain")
|
||||||
|
}
|
||||||
|
}
|
||||||
bail!(err);
|
bail!(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -249,6 +249,58 @@ pub fn current_epoch(
|
|||||||
ExpiredTimelocks::None
|
ExpiredTimelocks::None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bitcoin error codes: https://github.com/bitcoin/bitcoin/blob/97d3500601c1d28642347d014a6de1e38f53ae4e/src/rpc/protocol.h#L23
|
||||||
|
pub enum RpcErrorCode {
|
||||||
|
/// Transaction or block was rejected by network rules. Error code -26.
|
||||||
|
RpcVerifyRejected,
|
||||||
|
/// Transaction or block was rejected by network rules. Error code -27.
|
||||||
|
RpcVerifyAlreadyInChain,
|
||||||
|
/// General error during transaction or block submission
|
||||||
|
RpcVerifyError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RpcErrorCode> for i64 {
|
||||||
|
fn from(code: RpcErrorCode) -> Self {
|
||||||
|
match code {
|
||||||
|
RpcErrorCode::RpcVerifyError => -25,
|
||||||
|
RpcErrorCode::RpcVerifyRejected => -26,
|
||||||
|
RpcErrorCode::RpcVerifyAlreadyInChain => -27,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_rpc_error_code(error: &anyhow::Error) -> anyhow::Result<i64> {
|
||||||
|
let string = match error.downcast_ref::<bdk::Error>() {
|
||||||
|
Some(bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(
|
||||||
|
serde_json::Value::String(string),
|
||||||
|
))) => string,
|
||||||
|
_ => bail!("Error is of incorrect variant:{}", error),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::from_str(&string.replace("sendrawtransaction RPC error:", ""))?;
|
||||||
|
|
||||||
|
let json_map = match json {
|
||||||
|
serde_json::Value::Object(map) => map,
|
||||||
|
_ => bail!("Json error is not json object "),
|
||||||
|
};
|
||||||
|
|
||||||
|
let error_code_value = match json_map.get("code") {
|
||||||
|
Some(val) => val,
|
||||||
|
None => bail!("No error code field"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let error_code_number = match error_code_value {
|
||||||
|
serde_json::Value::Number(num) => num,
|
||||||
|
_ => bail!("Error code is not a number"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(int) = error_code_number.as_i64() {
|
||||||
|
Ok(int)
|
||||||
|
} else {
|
||||||
|
bail!("Error code is not an unsigned integer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, thiserror::Error, Debug)]
|
#[derive(Clone, Copy, thiserror::Error, Debug)]
|
||||||
#[error("transaction does not spend anything")]
|
#[error("transaction does not spend anything")]
|
||||||
pub struct NoInputs;
|
pub struct NoInputs;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::bitcoin::{Txid, Wallet};
|
use crate::bitcoin::{parse_rpc_error_code, RpcErrorCode, Txid, Wallet};
|
||||||
use crate::database::{Database, Swap};
|
use crate::database::{Database, Swap};
|
||||||
use crate::protocol::bob::BobState;
|
use crate::protocol::bob::BobState;
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
@ -38,9 +38,11 @@ pub async fn cancel(
|
|||||||
let txid = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
let txid = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
|
||||||
Ok(txid) => txid,
|
Ok(txid) => txid,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(bdk::Error::TransactionConfirmed) = err.downcast_ref::<bdk::Error>() {
|
if let Ok(code) = parse_rpc_error_code(&err) {
|
||||||
tracing::info!("Cancel transaction has already been published and confirmed")
|
if code == i64::from(RpcErrorCode::RpcVerifyAlreadyInChain) {
|
||||||
};
|
tracing::info!("Cancel transaction has already been confirmed on chain")
|
||||||
|
}
|
||||||
|
}
|
||||||
bail!(err);
|
bail!(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ use harness::alice_run_until::is_xmr_lock_transaction_sent;
|
|||||||
use harness::bob_run_until::is_btc_locked;
|
use harness::bob_run_until::is_btc_locked;
|
||||||
use harness::SlowCancelConfig;
|
use harness::SlowCancelConfig;
|
||||||
use swap::asb::FixedRate;
|
use swap::asb::FixedRate;
|
||||||
|
use swap::bitcoin::{parse_rpc_error_code, RpcErrorCode};
|
||||||
use swap::protocol::alice::AliceState;
|
use swap::protocol::alice::AliceState;
|
||||||
use swap::protocol::bob::BobState;
|
use swap::protocol::bob::BobState;
|
||||||
use swap::protocol::{alice, bob};
|
use swap::protocol::{alice, bob};
|
||||||
@ -41,10 +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)
|
let error = cli::cancel(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
match error.downcast::<bdk::Error>().unwrap() {
|
assert_eq!(
|
||||||
bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(..)) => (),
|
parse_rpc_error_code(&error).unwrap(),
|
||||||
unexpected => panic!("Failed to cancel due to unexpected error: {}", unexpected),
|
i64::from(RpcErrorCode::RpcVerifyRejected)
|
||||||
}
|
);
|
||||||
|
|
||||||
ctx.restart_alice().await;
|
ctx.restart_alice().await;
|
||||||
let alice_swap = ctx.alice_next_swap().await;
|
let alice_swap = ctx.alice_next_swap().await;
|
||||||
@ -54,9 +55,13 @@ async fn given_alice_and_bob_manually_cancel_when_timelock_not_expired_errors()
|
|||||||
));
|
));
|
||||||
|
|
||||||
// Alice tries but fails manual cancel
|
// Alice tries but fails manual cancel
|
||||||
let result =
|
let error = asb::cancel(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db)
|
||||||
asb::cancel(alice_swap.swap_id, alice_swap.bitcoin_wallet, alice_swap.db).await;
|
.await
|
||||||
assert!(result.is_err());
|
.unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
parse_rpc_error_code(&error).unwrap(),
|
||||||
|
i64::from(RpcErrorCode::RpcVerifyRejected)
|
||||||
|
);
|
||||||
|
|
||||||
let (bob_swap, bob_join_handle) = ctx
|
let (bob_swap, bob_join_handle) = ctx
|
||||||
.stop_and_resume_bob_from_db(bob_join_handle, swap_id)
|
.stop_and_resume_bob_from_db(bob_join_handle, swap_id)
|
||||||
@ -67,10 +72,10 @@ async fn given_alice_and_bob_manually_cancel_when_timelock_not_expired_errors()
|
|||||||
let error = cli::refund(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db)
|
let error = cli::refund(bob_swap.id, bob_swap.bitcoin_wallet, bob_swap.db)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
match error.downcast::<bdk::Error>().unwrap() {
|
assert_eq!(
|
||||||
bdk::Error::Electrum(bdk::electrum_client::Error::Protocol(..)) => (),
|
parse_rpc_error_code(&error).unwrap(),
|
||||||
unexpected => panic!("Failed to refund due to unexpected error: {}", unexpected),
|
i64::from(RpcErrorCode::RpcVerifyError)
|
||||||
}
|
);
|
||||||
|
|
||||||
let (bob_swap, _) = ctx
|
let (bob_swap, _) = ctx
|
||||||
.stop_and_resume_bob_from_db(bob_join_handle, swap_id)
|
.stop_and_resume_bob_from_db(bob_join_handle, swap_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user