Skip to content
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ This changelog covers Firehose-specific changes only. For upstream reth changes,

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased
## reth-v2.7.0-fh3.1-1

### Fixed

- Fixed glamsterdam hard fork crash when BAL executes in parallel (when a block has more than 30 transactions)

### Changed

Expand Down
7 changes: 6 additions & 1 deletion crates/engine/tree/src/tree/payload_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,10 @@ where
// Get an iterator over the transactions in the payload
let txs = self.tx_iterator_for(&input)?;

let parallel_bal_execution = ensure_ok!(self.bal_path_eligible(env.decoded_bal.as_deref()));
// Firehose executes sequentially, and in BAL mode the payload processor streams
// transactions in conversion-completion order instead of block order.
let parallel_bal_execution = ensure_ok!(self.bal_path_eligible(env.decoded_bal.as_deref())) &&
!reth_firehose::is_tracer_initialized();

// Prepare the state-root job before execution so it can provide streaming hooks.
let mut state_root_job =
Expand Down Expand Up @@ -762,6 +765,8 @@ where
//
// BAL parallel execution is incompatible with sequential Firehose tracing, so a block
// carrying a BAL is executed sequentially here, building its BAL like `execute_block`.
// `parallel_bal_execution` is forced off while tracing so the transactions arrive in
// block order.
match make_state_provider(false) {
Ok(state_provider) => self.execute_and_trace_block(
state_provider,
Expand Down
31 changes: 30 additions & 1 deletion crates/engine/tree/tests/firehose_live_tracing_amsterdam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
//! call the same way the pipeline path does (see the `amsterdam_create_runtime_out_of_gas`
//! prestate case in `reth-firehose-tests`).
//!
//! The last block carries enough transactions from a single sender for the payload processor to
//! convert them in parallel. The traced execution is sequential, so it must still receive them in
//! block order or the sender's later nonces are rejected as too high.
//!
//! Like `firehose_live_tracing`, it lives in its own integration-test binary because it installs
//! a process-wide tracer.

Expand All @@ -19,7 +23,11 @@ use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
use eyre::Result;
use firehose_tracer::pb::sf::ethereum::r#type::v2::{CallType, TransactionTraceStatus};
use reth_chainspec::{EthChainSpec, EthereumHardfork};
use reth_e2e_test_utils::{transaction::TransactionTestContext, wallet::Wallet, E2ETestSetupExt};
use reth_e2e_test_utils::{
transaction::TransactionTestContext,
wallet::{test_signer, Wallet},
E2ETestSetupExt,
};
use reth_node_ethereum::EthereumNode;
use reth_provider::BalProvider;

Expand All @@ -32,6 +40,12 @@ const FRAMELESS_BLOCK: u64 = 2;
/// Above Amsterdam's intrinsic cost for the init code below, far below its creation state gas.
const FRAMELESS_GAS_LIMIT: u64 = 30_000;

/// Block that carries the same-sender batch.
const BATCH_BLOCK: u64 = 3;

/// Above the payload processor's sequential conversion threshold of 30 transactions.
const BATCH_TX_COUNT: u64 = 40;

#[tokio::test]
async fn live_payload_validation_traces_amsterdam_blocks() -> Result<()> {
reth_tracing::init_test_tracing();
Expand Down Expand Up @@ -66,6 +80,17 @@ async fn live_payload_validation_traces_amsterdam_blocks() -> Result<()> {
TransactionTestContext::sign_tx_bytes(Wallet::default().inner, deploy).await;
node.rpc.inject_tx(deploy).await?;
}
if number == BATCH_BLOCK {
for nonce in 0..BATCH_TX_COUNT {
let transfer = TransactionTestContext::transfer_tx_bytes_with_nonce(
chain_id,
test_signer(1),
nonce,
)
.await;
node.rpc.inject_tx(transfer).await?;
}
}
let payload = node.advance_block().await?;
hashes.push(payload.block().hash());
}
Expand Down Expand Up @@ -100,6 +125,10 @@ async fn live_payload_validation_traces_amsterdam_blocks() -> Result<()> {
assert_eq!(root.call_type, CallType::Create as i32);
assert!(root.status_failed);

let block = reth_firehose_tests::parse_fire_block_for(&buffer.get_bytes(), BATCH_BLOCK)?;
let nonces = block.transaction_traces.iter().map(|trx| trx.nonce).collect::<Vec<_>>();
assert_eq!(nonces, (0..BATCH_TX_COUNT).collect::<Vec<_>>());

// The BAL built by the traced execution is what the node stores for the block.
for hash in hashes {
let bal = node.inner.provider.get_bal_by_hash(hash)?;
Expand Down
Loading