Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:add rpc debug_traceCallMany #2974

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
15 changes: 14 additions & 1 deletion crates/client/src/rpc/impls/eth/debug.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -77,4 +77,17 @@ impl Debug for GethDebugHandler {
.trace_call(request, block_number, opts)
.map_err(|err| err.into())
}

fn debug_trace_call_many(
&self,
bundles: Vec<Bundle>,
simulation_context: SimulationContext,
// state_override: Option<StateOverride>,
// timeout: Option<Duration>,
opts: Option<GethDebugTracingCallOptions>,
) -> JsonRpcResult<Vec<GethTrace>> {
self.inner
.trace_call_many(bundles, simulation_context, opts)
.map_err(|err| err.into())
}
}
12 changes: 11 additions & 1 deletion crates/client/src/rpc/traits/eth_space/debug.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -38,4 +38,14 @@ pub trait Debug {
&self, request: TransactionRequest, block_number: Option<BlockNumber>,
opts: Option<GethDebugTracingCallOptions>,
) -> JsonRpcResult<GethTrace>;

#[rpc(name = "debug_traceCallMany")]
fn debug_trace_call_many(
&self,
bundles: Vec<Bundle>,
simulation_context: SimulationContext,
// state_override: Option<StateOverride>,
// timeout: Option<Duration>,
opts: Option<GethDebugTracingCallOptions>,
) -> JsonRpcResult<Vec<GethTrace>>;
}
4 changes: 2 additions & 2 deletions crates/client/src/rpc/types/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
10 changes: 9 additions & 1 deletion crates/rpc/rpc-eth-api/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -36,4 +36,12 @@ pub trait DebugApi {
&self, request: TransactionRequest, block_number: Option<BlockNumber>,
opts: Option<GethDebugTracingCallOptions>,
) -> RpcResult<GethTrace>;

#[method(name = "traceCallMany")]
async fn debug_trace_call_many(
&self,
bundles: Vec<Bundle>,
simulation_context: SimulationContext,
opts: Option<GethDebugTracingCallOptions>,
) -> RpcResult<Vec<GethTrace>>;
}
11 changes: 11 additions & 0 deletions crates/rpc/rpc-eth-types/src/bundle.rs
Original file line number Diff line number Diff line change
@@ -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<TransactionRequest>,
// /// BlockOverride
// pub block_override: Option<StateOverride>,
}
4 changes: 4 additions & 0 deletions crates/rpc/rpc-eth-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,13 +17,15 @@ 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::*;
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;
Expand Down
11 changes: 11 additions & 0 deletions crates/rpc/rpc-eth-types/src/simulation_context.rs
Original file line number Diff line number Diff line change
@@ -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<BlockNumber>,
// /// TransactionIndex
// pub transaction_index: Option<U256>,
}
146 changes: 144 additions & 2 deletions crates/rpc/rpc/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ 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 cfx_types::{AddressSpaceUtil, Space, H256, U256, H160};
use cfxcore::{
errors::Error as CoreError, ConsensusGraph, ConsensusGraphTrait,
SharedConsensusGraph,
Expand All @@ -19,6 +19,7 @@ use primitives::{
Block, BlockHashOrEpochNumber, BlockHeaderBuilder, EpochNumber,
};
use std::sync::Arc;
use std::collections::HashMap;

pub struct DebugApi {
consensus: SharedConsensusGraph,
Expand Down Expand Up @@ -157,6 +158,135 @@ impl DebugApi {
Ok(res.trace.clone())
}

pub fn trace_call_many(
&self,
bundles: Vec<Bundle>,
simulation_context: SimulationContext,
opts: Option<GethDebugTracingCallOptions>,
) -> Result<Vec<GethTrace>, CoreError> {
let opts = opts.unwrap_or_default();
let block_num = simulation_context.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::new();
// manually manage nonce
let mut nonce_map: HashMap<Option<H160>, Option<U256>> = 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 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<Arc<Block>> = 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<GethDebugTracingOptions>,
) -> Result<Vec<TraceResult>, CoreError> {
Expand Down Expand Up @@ -287,4 +417,16 @@ impl DebugApiServer for DebugApi {
self.trace_call(request, block_number, opts)
.map_err(|e| e.into())
}

async fn debug_trace_call_many(
&self,
bundles: Vec<Bundle>,
simulation_context: SimulationContext,
// state_override: Option<StateOverride>,
// timeout: Option<Duration>,
opts: Option<GethDebugTracingCallOptions>,
) -> RpcResult<Vec<GethTrace>> {
self.trace_call_many(bundles, simulation_context, opts)
.map_err(|e| e.into())
}
}