From 1c3e323f40de0fa65d7d36ea18bdb3cd07d1d3ec Mon Sep 17 00:00:00 2001 From: xiehou3131 Date: Mon, 2 Dec 2024 08:28:15 +0000 Subject: [PATCH 1/4] feat:add rpc debug_traceCallMany --- crates/client/src/rpc/impls/eth/debug.rs | 10 ++ .../client/src/rpc/traits/eth_space/debug.rs | 7 ++ crates/rpc/rpc-eth-api/src/debug.rs | 7 ++ crates/rpc/rpc/src/debug.rs | 102 ++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 84e47d40f8..f97d6388a6 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -77,4 +77,14 @@ impl Debug for GethDebugHandler { .trace_call(request, block_number, opts) .map_err(|err| err.into()) } + + fn debug_trace_call_many( + &self, requests: Vec, + block_number: Option, + opts: Option, + ) -> JsonRpcResult> { + self.inner + .trace_call_many(requests, block_number, opts) + .map_err(|err| err.into()) + } } diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index f8efe19fe4..d1062a0df6 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -38,4 +38,11 @@ pub trait Debug { &self, request: TransactionRequest, block_number: Option, opts: Option, ) -> JsonRpcResult; + + #[rpc(name = "debug_traceCallMany")] + fn debug_trace_call_many( + &self, requests: Vec, + block_number: Option, + opts: Option, + ) -> JsonRpcResult>; } diff --git a/crates/rpc/rpc-eth-api/src/debug.rs b/crates/rpc/rpc-eth-api/src/debug.rs index 969a53fe67..11688df71d 100644 --- a/crates/rpc/rpc-eth-api/src/debug.rs +++ b/crates/rpc/rpc-eth-api/src/debug.rs @@ -36,4 +36,11 @@ pub trait DebugApi { &self, request: TransactionRequest, block_number: Option, opts: Option, ) -> RpcResult; + + #[method(name = "traceCallMany")] + async fn debug_trace_call_many( + &self, requests: Vec, + block_number: Option, + opts: Option, + ) -> RpcResult>; } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 2a2be11bc8..cb3ec4579b 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -157,6 +157,99 @@ impl DebugApi { Ok(res.trace.clone()) } + pub fn trace_call_many( + &self, requests: Vec, + block_number: Option, + opts: Option, + ) -> Result, CoreError> { + let opts = opts.unwrap_or_default(); + let block_num = block_number.unwrap_or_default(); + + let epoch_num = self + .get_block_epoch_num(block_num) + .map_err(|err| CoreError::Msg(err))?; + + // validate epoch state + self.consensus_graph() + .validate_stated_epoch(&EpochNumber::Number(epoch_num)) + .map_err(|err| CoreError::Msg(err))?; + + let epoch_block_hashes = self + .consensus_graph() + .get_block_hashes_by_epoch(EpochNumber::Number(epoch_num)) + .map_err(|err| CoreError::Msg(err))?; + let epoch_id = epoch_block_hashes + .last() + .ok_or(CoreError::Msg("should have block hash".to_string()))?; + + // construct blocks from call_request + let chain_id = self.consensus.best_chain_id(); + + // construct transactions + let mut transactions = Vec::with_capacity(requests.len()); + for mut request in requests { + if request.from.is_none() { + return Err(CoreError::InvalidParam( + "from is required".to_string(), + Default::default(), + )); + } + + // nonce auto fill + if request.nonce.is_none() { + let nonce = self.consensus_graph().next_nonce( + request.from.unwrap().with_evm_space(), + BlockHashOrEpochNumber::EpochNumber(EpochNumber::Number( + epoch_num, + )), + "num", + )?; + request.nonce = Some(nonce); + } + + let signed_tx = request.sign_call( + chain_id.in_evm_space(), + self.max_estimation_gas_limit, + )?; + + transactions.push(Arc::new(signed_tx)); + } + + let epoch_blocks = self + .consensus_graph() + .data_man + .blocks_by_hash_list( + &epoch_block_hashes, + true, /* update_cache */ + ) + .ok_or(CoreError::Msg("blocks should exist".to_string()))?; + let pivot_block = epoch_blocks + .last() + .ok_or(CoreError::Msg("should have block".to_string()))?; + + let header = BlockHeaderBuilder::new() + .with_base_price(pivot_block.block_header.base_price()) + .with_parent_hash(pivot_block.block_header.hash()) + .with_height(epoch_num + 1) + .with_timestamp(pivot_block.block_header.timestamp() + 1) + .with_gas_limit(*pivot_block.block_header.gas_limit()) + .build(); + let block = Block::new(header, transactions); + let blocks: Vec> = vec![Arc::new(block)]; + + let traces = self.consensus_graph().collect_blocks_geth_trace( + *epoch_id, + epoch_num, + &blocks, + opts.tracing_options, + None, + )?; + + let result = traces.iter().map(|val| val.trace.clone()).collect(); + + Ok(result) + } + pub fn trace_block_by_num( &self, block_num: u64, opts: Option, ) -> Result, CoreError> { @@ -287,4 +380,13 @@ impl DebugApiServer for DebugApi { self.trace_call(request, block_number, opts) .map_err(|e| e.into()) } + + async fn debug_trace_call_many( + &self, requests: Vec, + block_number: Option, + opts: Option, + ) -> RpcResult> { + self.trace_call_many(requests, block_number, opts) + .map_err(|e| e.into()) + } } From bdad063c0391b2f08409a0be056565b77a1e82e0 Mon Sep 17 00:00:00 2001 From: xiehou3131 Date: Thu, 5 Dec 2024 11:48:34 +0800 Subject: [PATCH 2/4] rewrite debug_traceCallMany to make it compatible with ethereum impl: --- crates/client/src/rpc/impls/eth/debug.rs | 11 ++++++---- .../client/src/rpc/traits/eth_space/debug.rs | 9 +++++--- crates/client/src/rpc/types/eth/mod.rs | 4 ++-- crates/rpc/rpc-eth-api/src/debug.rs | 9 +++++--- crates/rpc/rpc-eth-types/src/bundle.rs | 11 ++++++++++ crates/rpc/rpc-eth-types/src/lib.rs | 4 ++++ .../rpc-eth-types/src/simulation_context.rs | 11 ++++++++++ crates/rpc/rpc/src/debug.rs | 21 ++++++++++++------- 8 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 crates/rpc/rpc-eth-types/src/bundle.rs create mode 100644 crates/rpc/rpc-eth-types/src/simulation_context.rs diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index f97d6388a6..49ccc9634e 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -1,7 +1,7 @@ use crate::rpc::{ errors::invalid_params_msg, traits::eth_space::debug::Debug, - types::eth::{BlockNumber, TransactionRequest}, + types::eth::{BlockNumber, Bundle, SimulationContext, TransactionRequest}, }; use alloy_rpc_types_trace::geth::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, @@ -79,12 +79,15 @@ impl Debug for GethDebugHandler { } fn debug_trace_call_many( - &self, requests: Vec, - block_number: Option, + &self, + bundle: Bundle, + simulation_context: SimulationContext, + // state_override: Option, + // timeout: Option, opts: Option, ) -> JsonRpcResult> { self.inner - .trace_call_many(requests, block_number, opts) + .trace_call_many(bundle, simulation_context, opts) .map_err(|err| err.into()) } } diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index d1062a0df6..957037ca11 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -1,4 +1,4 @@ -use crate::rpc::types::eth::{BlockNumber, TransactionRequest}; +use crate::rpc::types::eth::{BlockNumber, Bundle, SimulationContext, TransactionRequest}; use alloy_rpc_types_trace::geth::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, @@ -41,8 +41,11 @@ pub trait Debug { #[rpc(name = "debug_traceCallMany")] fn debug_trace_call_many( - &self, requests: Vec, - block_number: Option, + &self, + bundle: Bundle, + simulation_context: SimulationContext, + // state_override: Option, + // timeout: Option, opts: Option, ) -> JsonRpcResult>; } diff --git a/crates/client/src/rpc/types/eth/mod.rs b/crates/client/src/rpc/types/eth/mod.rs index 414b4ce3cc..3c01c886b6 100644 --- a/crates/client/src/rpc/types/eth/mod.rs +++ b/crates/client/src/rpc/types/eth/mod.rs @@ -6,7 +6,7 @@ pub use cfx_rpc_eth_types::{ eth_pubsub, trace::{LocalizedTrace, Res}, trace_filter::TraceFilter, - AccountPendingTransactions, Block, BlockNumber, EthRpcLogFilter, - FilterChanges, Header, Log, Receipt, SyncInfo, SyncStatus, Transaction, + AccountPendingTransactions, Block, BlockNumber, Bundle, EthRpcLogFilter, + FilterChanges, Header, Log, Receipt, SimulationContext, SyncInfo, SyncStatus, Transaction, TransactionRequest, }; diff --git a/crates/rpc/rpc-eth-api/src/debug.rs b/crates/rpc/rpc-eth-api/src/debug.rs index 11688df71d..dc06b339ee 100644 --- a/crates/rpc/rpc-eth-api/src/debug.rs +++ b/crates/rpc/rpc-eth-api/src/debug.rs @@ -2,7 +2,7 @@ use alloy_rpc_types_trace::geth::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, }; -use cfx_rpc_eth_types::{BlockNumber, TransactionRequest}; +use cfx_rpc_eth_types::{BlockNumber, Bundle, SimulationContext, TransactionRequest}; use cfx_types::H256; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; @@ -39,8 +39,11 @@ pub trait DebugApi { #[method(name = "traceCallMany")] async fn debug_trace_call_many( - &self, requests: Vec, - block_number: Option, + &self, + bundle: Bundle, + simulation_context: SimulationContext, + // state_override: Option, + // timeout: Option, opts: Option, ) -> RpcResult>; } diff --git a/crates/rpc/rpc-eth-types/src/bundle.rs b/crates/rpc/rpc-eth-types/src/bundle.rs new file mode 100644 index 0000000000..cb75e8d1b7 --- /dev/null +++ b/crates/rpc/rpc-eth-types/src/bundle.rs @@ -0,0 +1,11 @@ +use crate::TransactionRequest; +use serde::Deserialize; + +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Bundle { + /// Transactions + pub transactions: Vec, + // /// BlockOverride + // pub block_override: Option, +} \ No newline at end of file diff --git a/crates/rpc/rpc-eth-types/src/lib.rs b/crates/rpc/rpc-eth-types/src/lib.rs index 866c0bff2a..8680603001 100644 --- a/crates/rpc/rpc-eth-types/src/lib.rs +++ b/crates/rpc/rpc-eth-types/src/lib.rs @@ -1,11 +1,13 @@ mod block; mod block_number; +mod bundle; mod errors; pub mod eth_pubsub; mod fee_history; mod filter; mod log; mod receipt; +mod simulation_context; mod sync; pub mod trace; pub mod trace_filter; @@ -15,6 +17,7 @@ mod tx_pool; pub use block::{Block, Header}; pub use block_number::BlockNumber; +pub use bundle::Bundle; pub use cfx_rpc_primitives::{Bytes, U64}; pub use errors::Error; pub use eth_pubsub::*; @@ -22,6 +25,7 @@ pub use fee_history::FeeHistory; pub use filter::*; pub use log::Log; pub use receipt::Receipt; +pub use simulation_context::SimulationContext; pub use sync::{SyncInfo, SyncStatus}; pub use trace::*; pub use trace_filter::TraceFilter; diff --git a/crates/rpc/rpc-eth-types/src/simulation_context.rs b/crates/rpc/rpc-eth-types/src/simulation_context.rs new file mode 100644 index 0000000000..376f228019 --- /dev/null +++ b/crates/rpc/rpc-eth-types/src/simulation_context.rs @@ -0,0 +1,11 @@ +use crate::BlockNumber; +use serde::Deserialize; + +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulationContext { + /// BlockNumber + pub block_number: Option, + // /// TransactionIndex + // pub transaction_index: Option, +} \ No newline at end of file diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index cb3ec4579b..7fd335379b 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -6,7 +6,7 @@ use alloy_rpc_types_trace::geth::{ }; use async_trait::async_trait; use cfx_rpc_eth_api::DebugApiServer; -use cfx_rpc_eth_types::{BlockNumber, TransactionRequest}; +use cfx_rpc_eth_types::{BlockNumber, Bundle, SimulationContext,TransactionRequest}; use cfx_rpc_utils::error::jsonrpsee_error_helpers::invalid_params_msg; use cfx_types::{AddressSpaceUtil, Space, H256, U256}; use cfxcore::{ @@ -158,12 +158,16 @@ impl DebugApi { } pub fn trace_call_many( - &self, requests: Vec, - block_number: Option, + &self, + bundle: Bundle, + simulation_context: SimulationContext, + // state_override: Option, + // timeout: Option, opts: Option, ) -> Result, CoreError> { + let requests = bundle.transactions; let opts = opts.unwrap_or_default(); - let block_num = block_number.unwrap_or_default(); + let block_num = simulation_context.block_number.unwrap_or_default(); let epoch_num = self .get_block_epoch_num(block_num) @@ -382,11 +386,14 @@ impl DebugApiServer for DebugApi { } async fn debug_trace_call_many( - &self, requests: Vec, - block_number: Option, + &self, + bundle: Bundle, + simulation_context: SimulationContext, + // state_override: Option, + // timeout: Option, opts: Option, ) -> RpcResult> { - self.trace_call_many(requests, block_number, opts) + self.trace_call_many(bundle, simulation_context, opts) .map_err(|e| e.into()) } } From 0eed2b545beb38c532c5ce0a2ae2d4a50de98136 Mon Sep 17 00:00:00 2001 From: xiehou3131 Date: Tue, 10 Dec 2024 16:59:00 +0800 Subject: [PATCH 3/4] change nonce to required field --- crates/rpc/rpc/src/debug.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 7fd335379b..9df70e2631 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -161,8 +161,6 @@ impl DebugApi { &self, bundle: Bundle, simulation_context: SimulationContext, - // state_override: Option, - // timeout: Option, opts: Option, ) -> Result, CoreError> { let requests = bundle.transactions; @@ -191,7 +189,7 @@ impl DebugApi { // construct transactions let mut transactions = Vec::with_capacity(requests.len()); - for mut request in requests { + for request in requests { if request.from.is_none() { return Err(CoreError::InvalidParam( "from is required".to_string(), @@ -201,14 +199,10 @@ impl DebugApi { // nonce auto fill if request.nonce.is_none() { - let nonce = self.consensus_graph().next_nonce( - request.from.unwrap().with_evm_space(), - BlockHashOrEpochNumber::EpochNumber(EpochNumber::Number( - epoch_num, - )), - "num", - )?; - request.nonce = Some(nonce); + return Err(CoreError::InvalidParam( + "nonce is required".to_string(), + Default::default(), + )); } let signed_tx = request.sign_call( From ef896156df05c1b662918d66e79a0a486634aa11 Mon Sep 17 00:00:00 2001 From: xiehou3131 Date: Wed, 11 Dec 2024 11:15:10 +0000 Subject: [PATCH 4/4] add nonce auto fill, change param Bundle to Vec --- crates/client/src/rpc/impls/eth/debug.rs | 4 +- .../client/src/rpc/traits/eth_space/debug.rs | 2 +- crates/rpc/rpc-eth-api/src/debug.rs | 4 +- crates/rpc/rpc/src/debug.rs | 93 +++++++++++++------ 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 49ccc9634e..d580e5cdaf 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -80,14 +80,14 @@ impl Debug for GethDebugHandler { fn debug_trace_call_many( &self, - bundle: Bundle, + bundles: Vec, simulation_context: SimulationContext, // state_override: Option, // timeout: Option, opts: Option, ) -> JsonRpcResult> { self.inner - .trace_call_many(bundle, simulation_context, opts) + .trace_call_many(bundles, simulation_context, opts) .map_err(|err| err.into()) } } diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index 957037ca11..f5ff13a4b7 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -42,7 +42,7 @@ pub trait Debug { #[rpc(name = "debug_traceCallMany")] fn debug_trace_call_many( &self, - bundle: Bundle, + bundles: Vec, simulation_context: SimulationContext, // state_override: Option, // timeout: Option, diff --git a/crates/rpc/rpc-eth-api/src/debug.rs b/crates/rpc/rpc-eth-api/src/debug.rs index dc06b339ee..cec79eab6c 100644 --- a/crates/rpc/rpc-eth-api/src/debug.rs +++ b/crates/rpc/rpc-eth-api/src/debug.rs @@ -40,10 +40,8 @@ pub trait DebugApi { #[method(name = "traceCallMany")] async fn debug_trace_call_many( &self, - bundle: Bundle, + bundles: Vec, simulation_context: SimulationContext, - // state_override: Option, - // timeout: Option, opts: Option, ) -> RpcResult>; } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 9df70e2631..78ba1606f6 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; use cfx_rpc_eth_api::DebugApiServer; use cfx_rpc_eth_types::{BlockNumber, Bundle, SimulationContext,TransactionRequest}; use cfx_rpc_utils::error::jsonrpsee_error_helpers::invalid_params_msg; -use cfx_types::{AddressSpaceUtil, Space, H256, U256}; +use cfx_types::{AddressSpaceUtil, Space, H256, U256, H160}; use cfxcore::{ errors::Error as CoreError, ConsensusGraph, ConsensusGraphTrait, SharedConsensusGraph, @@ -19,6 +19,7 @@ use primitives::{ Block, BlockHashOrEpochNumber, BlockHeaderBuilder, EpochNumber, }; use std::sync::Arc; +use std::collections::HashMap; pub struct DebugApi { consensus: SharedConsensusGraph, @@ -159,11 +160,10 @@ impl DebugApi { pub fn trace_call_many( &self, - bundle: Bundle, + bundles: Vec, simulation_context: SimulationContext, opts: Option, ) -> Result, CoreError> { - let requests = bundle.transactions; let opts = opts.unwrap_or_default(); let block_num = simulation_context.block_number.unwrap_or_default(); @@ -188,29 +188,67 @@ impl DebugApi { let chain_id = self.consensus.best_chain_id(); // construct transactions - let mut transactions = Vec::with_capacity(requests.len()); - for request in requests { - if request.from.is_none() { - return Err(CoreError::InvalidParam( - "from is required".to_string(), - Default::default(), - )); - } - - // nonce auto fill - if request.nonce.is_none() { - return Err(CoreError::InvalidParam( - "nonce is required".to_string(), - Default::default(), - )); + let mut transactions = Vec::new(); + // manually manage nonce + let mut nonce_map: HashMap, Option> = HashMap::new(); + + for bundle in bundles { + let requests = bundle.transactions; + + for mut request in requests { + if request.from.is_none() { + return Err(CoreError::InvalidParam( + "from is required".to_string(), + Default::default(), + )); + } + + // nonce auto fill + if request.nonce.is_none() { + if let Some(value) = nonce_map.get(&request.from) { + let nonce = value.unwrap() + U256::from(1); + request.nonce = Some(nonce); + nonce_map.insert(request.from, request.nonce); + } else { + let nonce = self.consensus_graph().next_nonce( + request.from.unwrap().with_evm_space(), + BlockHashOrEpochNumber::EpochNumber(EpochNumber::Number( + epoch_num, + )), + "num", + )?; + request.nonce = Some(nonce); + nonce_map.insert(request.from, request.nonce); + } + } else { + if let Some(value) = nonce_map.get(&request.from) { + let nonce = value.unwrap() + U256::from(1); + if request.nonce.unwrap() != nonce { + continue; + } + nonce_map.insert(request.from, request.nonce); + } else { + let nonce = self.consensus_graph().next_nonce( + request.from.unwrap().with_evm_space(), + BlockHashOrEpochNumber::EpochNumber(EpochNumber::Number( + epoch_num, + )), + "num", + )?; + if request.nonce.unwrap() != nonce { + continue; + } + nonce_map.insert(request.from, request.nonce); + } + } + + let signed_tx = request.sign_call( + chain_id.in_evm_space(), + self.max_estimation_gas_limit, + )?; + + transactions.push(Arc::new(signed_tx)); } - - let signed_tx = request.sign_call( - chain_id.in_evm_space(), - self.max_estimation_gas_limit, - )?; - - transactions.push(Arc::new(signed_tx)); } let epoch_blocks = self @@ -248,6 +286,7 @@ impl DebugApi { Ok(result) } + pub fn trace_block_by_num( &self, block_num: u64, opts: Option, ) -> Result, CoreError> { @@ -381,13 +420,13 @@ impl DebugApiServer for DebugApi { async fn debug_trace_call_many( &self, - bundle: Bundle, + bundles: Vec, simulation_context: SimulationContext, // state_override: Option, // timeout: Option, opts: Option, ) -> RpcResult> { - self.trace_call_many(bundle, simulation_context, opts) + self.trace_call_many(bundles, simulation_context, opts) .map_err(|e| e.into()) } }