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: support traceBlock and traceTransaction for bsc #101

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 1 addition & 31 deletions crates/bsc/consensus/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use crate::EXTRA_SEAL_LEN;
use alloy_rlp::Encodable;
use reth_primitives::{
keccak256, system_contracts::SYSTEM_CONTRACTS_SET, Address, BufMut, BytesMut, Header,
TransactionSigned, B256, B64, U256,
};
use reth_primitives::{keccak256, BufMut, BytesMut, Header, B256, B64, U256};
use std::env;

const SECONDS_PER_DAY: u64 = 86400; // 24 * 60 * 60
Expand All @@ -21,24 +18,6 @@ pub fn is_breathe_block(last_block_time: u64, block_time: u64) -> bool {
last_block_time != 0 && !is_same_day_in_utc(last_block_time, block_time)
}

pub fn is_system_transaction(tx: &TransactionSigned, sender: Address, header: &Header) -> bool {
if let Some(to) = tx.to() {
if sender == header.beneficiary &&
is_invoke_system_contract(&to) &&
tx.max_fee_per_gas() == 0
{
return true;
}
}

false
}

/// whether the contract is system or not
pub fn is_invoke_system_contract(addr: &Address) -> bool {
SYSTEM_CONTRACTS_SET.contains(addr)
}

pub fn hash_with_chain_id(header: &Header, chain_id: u64) -> B256 {
let mut out = BytesMut::new();
encode_header_with_chain_id(header, &mut out, chain_id);
Expand Down Expand Up @@ -155,13 +134,4 @@ mod tests {
println!("encode hash: {:?}", hex::encode(hash.as_slice()));
assert_eq!(hex::encode(hash.as_slice()), expected_hash);
}

#[test]
fn test_is_system_contract() {
let addr1 = address!("0000000000000000000000000000000000001000");
let addr2 = address!("0000000000000000000000000000000000001100");

assert_eq!(super::is_invoke_system_contract(&addr1), true);
assert_eq!(super::is_invoke_system_contract(&addr2), false);
}
}
9 changes: 4 additions & 5 deletions crates/bsc/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use lazy_static::lazy_static;
use lru::LruCache;
use parking_lot::RwLock;
use reth_bsc_consensus::{
is_breathe_block, is_system_transaction, validate_block_post_execution, Parlia,
ValidatorElectionInfo, ValidatorsInfo,
is_breathe_block, validate_block_post_execution, Parlia, ValidatorElectionInfo, ValidatorsInfo,
};
use reth_chainspec::{BscHardforks, ChainSpec, EthereumHardforks};
use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError};
Expand All @@ -18,7 +17,7 @@ use reth_evm::{
};
use reth_primitives::{
parlia::{ParliaConfig, Snapshot, VoteAddress, CHECKPOINT_INTERVAL, DEFAULT_TURN_LENGTH},
system_contracts::{get_upgrade_system_contracts, SLASH_CONTRACT},
system_contracts::{get_upgrade_system_contracts, is_system_transaction, SLASH_CONTRACT},
Address, BlockNumber, BlockWithSenders, Bytes, Header, Receipt, Transaction, TransactionSigned,
B256, BSC_MAINNET, U256,
};
Expand Down Expand Up @@ -168,7 +167,7 @@ where
let mut system_txs = Vec::with_capacity(2); // Normally there are 2 system transactions.
let mut receipts = Vec::with_capacity(block.body.len());
for (sender, transaction) in block.transactions_with_sender() {
if is_system_transaction(transaction, *sender, &block.header) {
if is_system_transaction(transaction, *sender, block.beneficiary) {
system_txs.push(transaction.clone());
continue;
}
Expand Down Expand Up @@ -800,7 +799,7 @@ impl<P> SnapshotReader<P>
where
P: ParliaProvider,
{
pub fn new(provider: Arc<P>, parlia: Arc<Parlia>) -> Self {
pub const fn new(provider: Arc<P>, parlia: Arc<Parlia>) -> Self {
Self { provider, parlia }
}

Expand Down
2 changes: 1 addition & 1 deletion crates/net/peers/src/bootnodes/bsc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! BSC bootnodes

/// Bsc testnet boot nodes.
/// Bsc mainnet boot nodes.
pub static BSC_MAINNET_BOOTNODES: &[&str] = &[
"enode://433c8bfdf53a3e2268ccb1b829e47f629793291cbddf0c76ae626da802f90532251fc558e2e0d10d6725e759088439bf1cd4714716b03a259a35d4b2e4acfa7f@52.69.102.73:30311",
"enode://571bee8fb902a625942f10a770ccf727ae2ba1bab2a2b64e121594a99c9437317f6166a395670a00b7d93647eacafe598b6bbcef15b40b6d1a10243865a3e80f@35.73.84.120:30311",
Expand Down
29 changes: 27 additions & 2 deletions crates/primitives/src/system_contracts/mod.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/prune/prune/src/pruner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ impl<DB: Database, S> Pruner<DB, S> {

/// Prunes ancient sidecars data from the static file provider.
pub fn prune_ancient_sidecars(
&mut self,
&self,
provider: &DatabaseProviderRW<DB>,
tip_block_number: BlockNumber,
) {
Expand Down
4 changes: 4 additions & 0 deletions crates/rpc/rpc-eth-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ tokio.workspace = true
auto_impl.workspace = true
dyn-clone.workspace = true
tracing.workspace = true
cfg-if = "1.0.0"

[features]
client = ["jsonrpsee/client", "jsonrpsee/async-client"]
Expand All @@ -55,3 +56,6 @@ optimism = [
"reth-provider/optimism",
"reth-rpc-eth-types/optimism"
]
bsc = [
"reth-primitives/bsc",
]
121 changes: 113 additions & 8 deletions crates/rpc/rpc-eth-api/src/helpers/call.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
//! Loads a pending block from database. Helper trait for `eth_` transaction, call and trace RPC
//! methods.

use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
use cfg_if::cfg_if;
use futures::Future;
#[cfg(feature = "bsc")]
use reth_chainspec::BscHardforks;
use reth_evm::{ConfigureEvm, ConfigureEvmEnv};
#[cfg(feature = "bsc")]
use reth_primitives::system_contracts::get_upgrade_system_contracts;
#[cfg(feature = "bsc")]
use reth_primitives::system_contracts::is_system_transaction;
use reth_primitives::{
revm_primitives::{
BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HaltReason,
Expand All @@ -27,12 +35,14 @@ use reth_rpc_types::{
AccessListWithGasUsed, BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo,
TransactionRequest,
};
#[cfg(feature = "bsc")]
use revm::bsc::SYSTEM_ADDRESS;
#[cfg(feature = "bsc")]
use revm::db::AccountState::{NotExisting, Touched};
use revm::{Database, DatabaseCommit};
use revm_inspectors::access_list::AccessListInspector;
use tracing::trace;

use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};

/// Execution related functions for the [`EthApiServer`](crate::EthApiServer) trait in
/// the `eth_` namespace.
pub trait EthCall: Call + LoadPendingBlock {
Expand Down Expand Up @@ -410,6 +420,16 @@ pub trait Call: LoadState + SpawnBlocking {
let parent_block = block.parent_hash;
let block_txs = block.into_transactions_ecrecovered();

cfg_if! {
if #[cfg(feature = "bsc")] {
let parent_timestamp = self.block(parent_block.into()).await?
.map(|block| block.timestamp)
.ok_or_else(|| EthApiError::UnknownParentBlock)?;
} else {
let parent_timestamp = 0;
}
}

let this = self.clone();
self.spawn_with_state_at_block(parent_block.into(), move |state| {
let mut db = CacheDB::new(StateProviderDatabase::new(state));
Expand All @@ -421,13 +441,21 @@ pub trait Call: LoadState + SpawnBlocking {
block_env.clone(),
block_txs,
tx.hash,
parent_timestamp,
)?;

let env = EnvWithHandlerCfg::new_with_cfg_env(
cfg,
block_env,
Call::evm_config(&this).tx_env(&tx),
);
cfg_if! {
if #[cfg(feature = "bsc")] {
let mut tx_env = Call::evm_config(&this).tx_env(&tx);
if is_system_transaction(&tx, tx.signer(), block_env.coinbase) {
tx_env.bsc.is_system_transaction = Some(true);
};
} else {
let tx_env = Call::evm_config(&this).tx_env(&tx);
}
}

let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env);

let (res, _) = this.transact(&mut db, env)?;
f(tx_info, res, db)
Expand All @@ -444,30 +472,107 @@ pub trait Call: LoadState + SpawnBlocking {
///
/// Note: This assumes the target transaction is in the given iterator.
/// Returns the index of the target transaction in the given iterator.
#[allow(unused_variables)]
fn replay_transactions_until<DB>(
&self,
db: &mut CacheDB<DB>,
cfg: CfgEnvWithHandlerCfg,
block_env: BlockEnv,
transactions: impl IntoIterator<Item = TransactionSignedEcRecovered>,
target_tx_hash: B256,
parent_timestamp: u64,
) -> Result<usize, EthApiError>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
{
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default());
#[allow(clippy::redundant_clone)]
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env.clone(), Default::default());

let mut evm = self.evm_config().evm_with_env(db, env);
let mut index = 0;
#[cfg(feature = "bsc")]
let mut before_system_tx = true;

// try to upgrade system contracts before all txs if feynman is not active
#[cfg(feature = "bsc")]
if !self.provider().chain_spec().is_feynman_active_at_timestamp(block_env.timestamp.to()) {
let contracts = get_upgrade_system_contracts(
self.provider().chain_spec().as_ref(),
block_env.number.to(),
block_env.timestamp.to(),
parent_timestamp,
)
.expect("get upgrade system contracts failed");

for (k, v) in contracts {
let account = evm.db_mut().load_account(k)?;
if account.account_state == NotExisting {
account.account_state = Touched;
}
account.info.code_hash = v.clone().unwrap().hash_slow();
account.info.code = v;
}
}

for tx in transactions {
// check if the transaction is a system transaction
// this should be done before return
#[cfg(feature = "bsc")]
if before_system_tx && is_system_transaction(&tx, tx.signer(), block_env.coinbase) {
let sys_acc = evm.db_mut().load_account(SYSTEM_ADDRESS)?;
let balance = sys_acc.info.balance;
if balance > U256::ZERO {
sys_acc.info.balance = U256::ZERO;

let val_acc = evm.db_mut().load_account(block_env.coinbase)?;
if val_acc.account_state == NotExisting {
val_acc.account_state = Touched;
}
val_acc.info.balance += balance;
}

// try to upgrade system contracts between normal txs and system txs
// if feynman is active
if !self
.provider()
.chain_spec()
.is_feynman_active_at_timestamp(block_env.timestamp.to())
{
let contracts = get_upgrade_system_contracts(
self.provider().chain_spec().as_ref(),
block_env.number.to(),
block_env.timestamp.to(),
parent_timestamp,
)
.expect("get upgrade system contracts failed");

for (k, v) in contracts {
let account = evm.db_mut().load_account(k)?;
if account.account_state == NotExisting {
account.account_state = Touched;
}
account.info.code_hash = v.clone().unwrap().hash_slow();
account.info.code = v;
}
}

before_system_tx = false;
}

if tx.hash() == target_tx_hash {
// reached the target transaction
break
}

let sender = tx.signer();
self.evm_config().fill_tx_env(evm.tx_mut(), &tx.into_signed(), sender);

#[cfg(feature = "bsc")]
if !before_system_tx {
evm.tx_mut().bsc.is_system_transaction = Some(true);
};

evm.transact_commit()?;
index += 1;
}
Expand Down
31 changes: 26 additions & 5 deletions crates/rpc/rpc-eth-api/src/helpers/trace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Loads a pending block from database. Helper trait for `eth_` call and trace RPC methods.

use cfg_if::cfg_if;
use futures::Future;
use reth_evm::{ConfigureEvm, ConfigureEvmEnv};
#[cfg(feature = "bsc")]
use reth_primitives::system_contracts::is_system_transaction;
use reth_primitives::B256;
use reth_revm::database::StateProviderDatabase;
use reth_rpc_eth_types::{
Expand Down Expand Up @@ -183,6 +186,16 @@ pub trait Trace: LoadState {
let parent_block = block.parent_hash;
let block_txs = block.into_transactions_ecrecovered();

cfg_if! {
if #[cfg(feature = "bsc")] {
let parent_timestamp = LoadState::cache(self).get_block(parent_block).await?
.map(|block| block.timestamp)
.ok_or_else(|| EthApiError::UnknownParentBlock)?;
} else {
let parent_timestamp = 0;
}
}

let this = self.clone();
self.spawn_with_state_at_block(parent_block.into(), move |state| {
let mut db = CacheDB::new(StateProviderDatabase::new(state));
Expand All @@ -194,13 +207,21 @@ pub trait Trace: LoadState {
block_env.clone(),
block_txs,
tx.hash,
parent_timestamp,
)?;

let env = EnvWithHandlerCfg::new_with_cfg_env(
cfg,
block_env,
Call::evm_config(&this).tx_env(&tx),
);
cfg_if! {
if #[cfg(feature = "bsc")] {
let mut tx_env = Call::evm_config(&this).tx_env(&tx);
if is_system_transaction(&tx, tx.signer(), block_env.coinbase) {
tx_env.bsc.is_system_transaction = Some(true);
};
} else {
let tx_env = Call::evm_config(&this).tx_env(&tx);
}
}

let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env);
let (res, _) =
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
f(tx_info, inspector, res, db)
Expand Down
7 changes: 6 additions & 1 deletion crates/rpc/rpc-eth-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ pub enum EthApiError {
/// Thrown when an unknown block or transaction index is encountered
#[error("unknown block or tx index")]
UnknownBlockOrTxIndex,
/// Thrown when an unknown parent block is encountered
#[error("unknown parent block")]
UnknownParentBlock,
/// When an invalid block range is provided
#[error("invalid block range")]
InvalidBlockRange,
Expand Down Expand Up @@ -162,7 +165,9 @@ impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
EthApiError::EvmCustom(_) |
EthApiError::EvmPrecompile(_) |
EthApiError::InvalidRewardPercentiles => internal_rpc_err(error.to_string()),
EthApiError::UnknownBlockNumber | EthApiError::UnknownBlockOrTxIndex => {
EthApiError::UnknownBlockNumber |
EthApiError::UnknownBlockOrTxIndex |
EthApiError::UnknownParentBlock => {
rpc_error_with_code(EthRpcErrorCode::ResourceNotFound.code(), error.to_string())
}
EthApiError::UnknownSafeOrFinalizedBlock => {
Expand Down
Loading
Loading