From 47eafe11cd0424edfa134aa61ae1b3bae9d7aae6 Mon Sep 17 00:00:00 2001 From: Fuyin Date: Fri, 19 May 2023 15:17:49 +0800 Subject: [PATCH] feat: collect debug info for ckb tx pool on duplicate tx error --- crates/relayer/src/chain/ckb.rs | 63 +++++++++++-------- crates/relayer/src/chain/ckb/communication.rs | 7 ++- .../relayer/src/chain/ckb/mock_rpc_client.rs | 12 +++- crates/relayer/src/chain/ckb/rpc_client.rs | 10 ++- crates/relayer/src/chain/ckb/utils.rs | 38 +++++++++++ 5 files changed, 100 insertions(+), 30 deletions(-) diff --git a/crates/relayer/src/chain/ckb.rs b/crates/relayer/src/chain/ckb.rs index fa40b5293..3feb817e2 100644 --- a/crates/relayer/src/chain/ckb.rs +++ b/crates/relayer/src/chain/ckb.rs @@ -232,33 +232,44 @@ impl CkbChain { .map_err(Error::key_base)? .into_ckb_keypair(self.network()?); let tx = signer::sign(tx, &inputs, vec![], key).map_err(Error::key_base)?; - let hash = self - .rt - .block_on( - self.rpc_client - .send_transaction(&tx.data().into(), Some(OutputsValidator::Passthrough)), - ) - .map_err(|e| { - Error::send_tx(format!( - "{e}\n== transaction for debugging is below ==\n{}", - serde_json::to_string(&JsonTx::from(tx)).expect("jsonify ckb tx") - )) - })?; - - tracing::info!( - "ckb send_transaction success: {}, wait committed to block", - hex::encode(&hash) - ); - - self.rt.block_on(utils::wait_ckb_transaction_committed( - &self.rpc_client, - hash, - Duration::from_secs(3), - 0, - Duration::from_secs(30), - ))?; - Ok(()) + let task = async { + let send_res = self + .rpc_client + .send_transaction(&tx.data().into(), Some(OutputsValidator::Passthrough)) + .await; + let hash = match send_res { + Ok(hash) => Ok(hash), + Err(e) => { + let pool_log = utils::collect_ckb_tx_pool_info_on_duplicate_tx( + self.rpc_client.as_ref(), + &e, + ) + .await + .unwrap_or_default(); + let tx_info = format!( + "== transaction for debugging is below ==\n{}", + serde_json::to_string(&JsonTx::from(tx)).expect("jsonify ckb tx") + ); + Err(Error::send_tx(format!("{e}\n{pool_log}\n{tx_info}\n"))) + } + }?; + + tracing::info!( + "ckb send_transaction success: {}, wait committed to block", + hex::encode(&hash) + ); + + utils::wait_ckb_transaction_committed( + &self.rpc_client, + hash, + Duration::from_secs(3), + 0, + Duration::from_secs(30), + ) + .await + }; + self.rt.block_on(task) } pub fn network(&self) -> Result { diff --git a/crates/relayer/src/chain/ckb/communication.rs b/crates/relayer/src/chain/ckb/communication.rs index bc6addb74..2a7fdd83d 100644 --- a/crates/relayer/src/chain/ckb/communication.rs +++ b/crates/relayer/src/chain/ckb/communication.rs @@ -1,6 +1,6 @@ use ckb_jsonrpc_types::{ BlockNumber, BlockView, CellWithStatus, ChainInfo, HeaderView, JsonBytes, OutPoint, - OutputsValidator, Transaction, TransactionWithStatusResponse, + OutputsValidator, RawTxPool, Transaction, TransactionWithStatusResponse, TxPoolInfo, }; use ckb_sdk::rpc::ckb_indexer::{Cell, Pagination, SearchKey}; use ckb_types::H256; @@ -34,6 +34,11 @@ pub trait CkbReader { limit: u32, cursor: Option, ) -> Response>; + + // For debugging purposes. + fn get_raw_tx_pool(&self, verbose: bool) -> Response; + + fn tx_pool_info(&self) -> Response; } pub trait CkbWriter { diff --git a/crates/relayer/src/chain/ckb/mock_rpc_client.rs b/crates/relayer/src/chain/ckb/mock_rpc_client.rs index 46727b27a..03835895c 100644 --- a/crates/relayer/src/chain/ckb/mock_rpc_client.rs +++ b/crates/relayer/src/chain/ckb/mock_rpc_client.rs @@ -3,8 +3,8 @@ use ckb_jsonrpc_types::{ BlockNumber, BlockView, CellWithStatus, ChainInfo, Header, HeaderView, JsonBytes, OutPoint, - OutputsValidator, ResponseFormat, Transaction, TransactionView, TransactionWithStatusResponse, - TxStatus, + OutputsValidator, RawTxPool, ResponseFormat, Transaction, TransactionView, + TransactionWithStatusResponse, TxPoolInfo, TxStatus, }; use ckb_sdk::rpc::ckb_indexer::{Cell, Pagination, SearchKey}; use ckb_types::{packed, prelude::*, H256}; @@ -171,6 +171,14 @@ impl CkbReader for RpcClient { }; Box::pin(async { Ok(resp) }) } + + fn get_raw_tx_pool(&self, verbose: bool) -> Rpc { + todo!() + } + + fn tx_pool_info(&self) -> Rpc { + todo!() + } } impl CkbWriter for RpcClient { diff --git a/crates/relayer/src/chain/ckb/rpc_client.rs b/crates/relayer/src/chain/ckb/rpc_client.rs index 00658c6df..9695ff4e3 100644 --- a/crates/relayer/src/chain/ckb/rpc_client.rs +++ b/crates/relayer/src/chain/ckb/rpc_client.rs @@ -2,7 +2,7 @@ use ckb_jsonrpc_types::{ BlockNumber, BlockView, CellWithStatus, ChainInfo, HeaderView, JsonBytes, OutPoint, - OutputsValidator, Transaction, TransactionWithStatusResponse, Uint32, + OutputsValidator, RawTxPool, Transaction, TransactionWithStatusResponse, TxPoolInfo, Uint32, }; use ckb_sdk::rpc::ckb_indexer::{Cell, Order, Pagination, SearchKey}; use ckb_types::H256; @@ -162,6 +162,14 @@ impl CkbReader for RpcClient { ) .boxed() } + + fn get_raw_tx_pool(&self, verbose: bool) -> Rpc { + jsonrpc!("get_raw_tx_pool", Target::CKB, self, RawTxPool, verbose).boxed() + } + + fn tx_pool_info(&self) -> Rpc { + jsonrpc!("tx_pool_info", Target::CKB, self, TxPoolInfo).boxed() + } } impl CkbWriter for RpcClient { diff --git a/crates/relayer/src/chain/ckb/utils.rs b/crates/relayer/src/chain/ckb/utils.rs index 25d3fd49d..6a0da5d58 100644 --- a/crates/relayer/src/chain/ckb/utils.rs +++ b/crates/relayer/src/chain/ckb/utils.rs @@ -352,6 +352,44 @@ pub async fn wait_ckb_transaction_committed( Ok(()) } +pub async fn collect_ckb_tx_pool_info_on_duplicate_tx( + rpc: &impl CkbReader, + send_tx_err: &Error, +) -> Option { + let err_msg = format!("{send_tx_err}"); + // Collecting debug info for ckb tx pool on duplicate tx error. + // The error code is -1077. + // https://github.com/nervosnetwork/ckb/tree/develop/rpc#error-poolrejectedduplicatedtransaction + if err_msg.contains("PoolRejectedDuplicatedTransaction") || err_msg.contains("-1077") { + let mut pool_log = String::new(); + match rpc.get_raw_tx_pool(true).await { + Ok(raw_tx_pool) => { + pool_log += &format!( + "== CKB raw tx pool ==\n{}\n", + serde_json::to_string(&raw_tx_pool).expect("jsonify ckb raw tx pool") + ); + } + Err(e) => { + pool_log += &format!("== get ckb raw tx pool failed ==\n{e}"); + } + } + match rpc.tx_pool_info().await { + Ok(tx_pool_info) => { + pool_log += &format!( + "== CKB tx pool info ==\n{}", + serde_json::to_string(&tx_pool_info).expect("jsonify ckb tx pool info") + ); + } + Err(e) => { + pool_log += &format!("== get ckb tx pool info failed ==\n{e}"); + } + } + Some(pool_log) + } else { + None + } +} + #[cfg(test)] mod tests { use std::path::Path;