From 7bfc604a10379b484f636e6041cdf488cc94e7da Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 19 Jun 2024 07:13:20 +0800 Subject: [PATCH] fix(rpc/trace): include block rewards in trace_filter rpc (#8868) Signed-off-by: jsvisa Co-authored-by: Matthias Seitz --- crates/chainspec/src/spec.rs | 4 +- crates/rpc/rpc/src/trace.rs | 133 +++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 40 deletions(-) diff --git a/crates/chainspec/src/spec.rs b/crates/chainspec/src/spec.rs index b28c75778022..ff2407313c5d 100644 --- a/crates/chainspec/src/spec.rs +++ b/crates/chainspec/src/spec.rs @@ -851,8 +851,8 @@ impl ChainSpec { self.fork(Hardfork::Homestead).active_at_block(block_number) } - /// The Paris hardfork (merge) is activated via ttd. If we have knowledge of the block, this - /// function will return true if the block number is greater than or equal to the Paris + /// The Paris hardfork (merge) is activated via block number. If we have knowledge of the block, + /// this function will return true if the block number is greater than or equal to the Paris /// (merge) block. pub fn is_paris_active_at_block(&self, block_number: u64) -> Option { self.paris_block_and_final_difficulty.map(|(paris_block, _)| block_number >= paris_block) diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index fdbf7163bd9d..e959a367e3f3 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -6,8 +6,10 @@ use crate::eth::{ }; use async_trait::async_trait; use jsonrpsee::core::RpcResult as Result; -use reth_consensus_common::calc::{base_block_reward, block_reward, ommer_reward}; -use reth_primitives::{revm::env::tx_env_with_recovered, BlockId, Bytes, SealedHeader, B256, U256}; +use reth_consensus_common::calc::{ + base_block_reward, base_block_reward_pre_merge, block_reward, ommer_reward, +}; +use reth_primitives::{revm::env::tx_env_with_recovered, BlockId, Bytes, Header, B256, U256}; use reth_provider::{BlockReader, ChainSpecProvider, EvmEnvProvider, StateProviderFactory}; use reth_revm::database::StateProviderDatabase; use reth_rpc_api::TraceApiServer; @@ -266,7 +268,7 @@ where // find relevant blocks to trace let mut target_blocks = Vec::new(); - for block in blocks { + for block in &blocks { let mut transaction_indices = HashSet::new(); let mut highest_matching_index = 0; for (tx_idx, tx) in block.body.iter().enumerate() { @@ -308,11 +310,26 @@ where } let block_traces = futures::future::try_join_all(block_traces).await?; - let all_traces = block_traces + let mut all_traces = block_traces .into_iter() .flatten() .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter())) - .collect(); + .collect::>(); + + // add reward traces for all blocks + for block in &blocks { + if let Some(base_block_reward) = self.calculate_base_block_reward(&block.header)? { + all_traces.extend(self.extract_reward_traces( + &block.header, + &block.ommers, + base_block_reward, + )); + } else { + // no block reward, means we're past the Paris hardfork and don't expect any rewards + // because the blocks in ascending order + break + } + } Ok(all_traces) } @@ -362,36 +379,12 @@ where maybe_traces.map(|traces| traces.into_iter().flatten().collect::>()); if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) { - if let Some(header_td) = self.provider().header_td(&block.header.hash())? { - if let Some(base_block_reward) = base_block_reward( - self.provider().chain_spec().as_ref(), - block.header.number, - block.header.difficulty, - header_td, - ) { - let block_reward = block_reward(base_block_reward, block.ommers.len()); - traces.push(reward_trace( - &block.header, - RewardAction { - author: block.header.beneficiary, - reward_type: RewardType::Block, - value: U256::from(block_reward), - }, - )); - - for uncle in &block.ommers { - let uncle_reward = - ommer_reward(base_block_reward, block.header.number, uncle.number); - traces.push(reward_trace( - &block.header, - RewardAction { - author: uncle.beneficiary, - reward_type: RewardType::Uncle, - value: U256::from(uncle_reward), - }, - )); - } - } + if let Some(base_block_reward) = self.calculate_base_block_reward(&block.header)? { + traces.extend(self.extract_reward_traces( + &block.header, + &block.ommers, + base_block_reward, + )); } } @@ -481,10 +474,74 @@ where Ok(Some(BlockOpcodeGas { block_hash: block.hash(), - block_number: block.number, + block_number: block.header.number, transactions, })) } + + /// Calculates the base block reward for the given block: + /// + /// - if Paris hardfork is activated, no block rewards are given + /// - if Paris hardfork is not activated, calculate block rewards with block number only + /// - if Paris hardfork is unknown, calculate block rewards with block number and ttd + fn calculate_base_block_reward(&self, header: &Header) -> EthResult> { + let chain_spec = self.provider().chain_spec(); + let is_paris_activated = chain_spec.is_paris_active_at_block(header.number); + + Ok(match is_paris_activated { + Some(true) => None, + Some(false) => Some(base_block_reward_pre_merge(&chain_spec, header.number)), + None => { + // if Paris hardfork is unknown, we need to fetch the total difficulty at the + // block's height and check if it is pre-merge to calculate the base block reward + if let Some(header_td) = self.provider().header_td_by_number(header.number)? { + base_block_reward( + chain_spec.as_ref(), + header.number, + header.difficulty, + header_td, + ) + } else { + None + } + } + }) + } + + /// Extracts the reward traces for the given block: + /// - block reward + /// - uncle rewards + fn extract_reward_traces( + &self, + header: &Header, + ommers: &[Header], + base_block_reward: u128, + ) -> Vec { + let mut traces = Vec::with_capacity(ommers.len() + 1); + + let block_reward = block_reward(base_block_reward, ommers.len()); + traces.push(reward_trace( + header, + RewardAction { + author: header.beneficiary, + reward_type: RewardType::Block, + value: U256::from(block_reward), + }, + )); + + for uncle in ommers { + let uncle_reward = ommer_reward(base_block_reward, header.number, uncle.number); + traces.push(reward_trace( + header, + RewardAction { + author: uncle.beneficiary, + reward_type: RewardType::Uncle, + value: U256::from(uncle_reward), + }, + )); + } + traces + } } #[async_trait] @@ -628,9 +685,9 @@ struct TraceApiInner { /// Helper to construct a [`LocalizedTransactionTrace`] that describes a reward to the block /// beneficiary. -fn reward_trace(header: &SealedHeader, reward: RewardAction) -> LocalizedTransactionTrace { +fn reward_trace(header: &Header, reward: RewardAction) -> LocalizedTransactionTrace { LocalizedTransactionTrace { - block_hash: Some(header.hash()), + block_hash: Some(header.hash_slow()), block_number: Some(header.number), transaction_hash: None, transaction_position: None,