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:
rishflab 2021-09-09 13:32:00 +10:00
parent f511ff093c
commit 110a5d2229
4 changed files with 80 additions and 19 deletions

View file

@ -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::protocol::alice::AliceState;
use anyhow::{bail, Result};
@ -44,9 +44,11 @@ pub async fn cancel(
let txid = match state3.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
Ok(txid) => txid,
Err(err) => {
if let Some(bdk::Error::TransactionConfirmed) = err.downcast_ref::<bdk::Error>() {
tracing::info!("Cancel transaction has already been published and confirmed")
};
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);
}
};

View file

@ -249,6 +249,58 @@ pub fn current_epoch(
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)]
#[error("transaction does not spend anything")]
pub struct NoInputs;

View file

@ -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::protocol::bob::BobState;
use anyhow::{bail, Result};
@ -38,9 +38,11 @@ pub async fn cancel(
let txid = match state6.submit_tx_cancel(bitcoin_wallet.as_ref()).await {
Ok(txid) => txid,
Err(err) => {
if let Some(bdk::Error::TransactionConfirmed) = err.downcast_ref::<bdk::Error>() {
tracing::info!("Cancel transaction has already been published and confirmed")
};
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);
}
};