Skip to content

Conversation

@rezzmah
Copy link
Contributor

@rezzmah rezzmah commented Dec 23, 2025

BerachainBlockBuilder wraps the inner block builder to fix sender count mismatch for PoL transactions. The reth block builder doesn't recover senders for transactions that bypass the pool (system txs), so we extract senders from PoL transactions directly.

Invariants are strictly enforced based on Prague1 activation:

  • Pre-Prague1: num_transactions == num_senders (no PoL injection)
  • Post-Prague1: num_transactions > num_senders (PoL always injected)

Violations in either direction indicate a bug in block assembly.

Fixes: #129

TODO: Rebase onto #200 once merged

BerachainBlockBuilder wraps the inner block builder to fix sender
count mismatch for PoL transactions. The reth block builder doesn't
recover senders for transactions that bypass the pool (system txs),
so we extract senders from PoL transactions directly.

Invariants are strictly enforced based on Prague1 activation:
- Pre-Prague1: num_transactions == num_senders (no PoL injection)
- Post-Prague1: num_transactions > num_senders (PoL always injected)

Violations in either direction indicate a bug in block assembly.

Fixes: #129
@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

Adds reth-storage-api dependency and introduces BerachainBlockBuilder, a wrapper around generic BlockBuilder that fixes sender/transaction mismatches from PoL injections. Implements sender reconstruction logic across Prague1 activation boundaries and exposes create_block_builder method in EVM configuration to integrate the new builder into block construction.

Changes

Cohort / File(s) Summary
Dependency Management
Cargo.toml
Adds reth-storage-api dependency pointing to reth GitHub repository at rev 536bebfcd.
Block Builder Implementation
src/node/evm/builder.rs
Introduces public BerachainBlockBuilder<B> struct wrapping a generic BlockBuilder. Overrides finish() to inspect and correct sender/transaction counts: enforces invariants across Prague1 boundary, reconstructs senders from PoL transactions when post-Prague1 and count mismatches occur, and rebuilds block with corrected senders.
EVM Configuration
src/node/evm/config.rs
Adds new imports (State, Database, EvmFor, BlockExecutorFactory, InspectorFor, BasicBlockBuilder) and implements create_block_builder() method in ConfigureEvm trait for BerachainEvmConfig. Returns BerachainBlockBuilder wrapping a BasicBlockBuilder with proper executor and context wiring.
Module Organization
src/node/evm/mod.rs
Declares private builder module in the EVM assembly context.

Sequence Diagram(s)

sequenceDiagram
    actor BlockProducer
    participant BerachainBB as BerachainBlockBuilder
    participant InnerBB as Inner BlockBuilder
    participant Executor
    participant StateProvider
    participant PolTxs as PoL Transactions

    BlockProducer->>BerachainBB: finish(state_provider)
    
    BerachainBB->>InnerBB: finish(state_provider)
    InnerBB->>Executor: execute transactions
    InnerBB->>StateProvider: finalize state
    InnerBB-->>BerachainBB: BlockBuilderOutcome
    
    Note over BerachainBB: Inspect tx & sender counts
    alt Post-Prague1 && txs > senders
        rect rgb(200, 220, 255)
        BerachainBB->>PolTxs: Extract senders from PoL txs at block start
        PolTxs-->>BerachainBB: Recovered senders
        BerachainBB->>BerachainBB: Prepend recovered senders
        end
    else Pre-Prague1 && txs > senders
        BerachainBB->>BerachainBB: Error: invariant violated
    else Counts match or mismatch allowed
        BerachainBB->>BerachainBB: Use senders as-is
    end
    
    BerachainBB->>BerachainBB: Reconstruct block with corrected senders
    BerachainBB-->>BlockProducer: BlockBuilderOutcome
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • chore: remove dependency on forked alloy evm #145: Modifies EVM integration and Berachain-specific builder/factory surfaces (introduces or replaces BerachainEvmFactory types), complementing this PR's BerachainBlockBuilder and create_block_builder additions.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: introducing a wrapper around the block builder to fix sender recovery for PoL transactions.
Linked Issues check ✅ Passed The PR addresses issue #129 by introducing BerachainBlockBuilder to fix sender count mismatches in block receipts caused by PoL injections, aligning with the linked issue's objective.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the block builder wrapper for PoL sender recovery: new builder struct, config integration, and dependency addition for chain spec logic.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rezbera/block-assembler-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rezzmah rezzmah requested a review from fridrik01 December 23, 2025 13:56
@rezzmah rezzmah assigned calbera and unassigned calbera Dec 23, 2025
@rezzmah rezzmah requested a review from calbera December 23, 2025 14:00
@rezzmah
Copy link
Contributor Author

rezzmah commented Dec 23, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc9c527 and ec37b9f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • src/node/evm/builder.rs
  • src/node/evm/config.rs
  • src/node/evm/mod.rs
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.rs: No comments in code unless explicitly requested
Use custom error types over generic errors
Extract magic numbers into documented constants
Write succinct documentation focusing on 'why' rather than 'what'

Files:

  • src/node/evm/mod.rs
  • src/node/evm/builder.rs
  • src/node/evm/config.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: rezbera
Repo: berachain/bera-reth PR: 152
File: src/pool/transaction.rs:3-4
Timestamp: 2025-09-10T05:49:07.298Z
Learning: In reth v1.7.0 upgrade, BerachainPooledTransactionVariant type alias should use PooledTransactionVariant directly instead of EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarVariant>>.
🧬 Code graph analysis (2)
src/node/evm/builder.rs (3)
src/pool/transaction.rs (1)
  • transaction (151-153)
src/engine/mod.rs (1)
  • inner (210-212)
src/node/evm/config.rs (1)
  • chain_spec (75-77)
src/node/evm/config.rs (2)
src/node/evm/builder.rs (1)
  • new (55-57)
src/node/evm/assembler.rs (1)
  • new (34-36)
🔇 Additional comments (8)
src/node/evm/mod.rs (1)

5-5: LGTM!

The module declaration is properly placed and uses appropriate private visibility, consistent with other internal modules like assembler and block_context.

Cargo.toml (1)

65-65: LGTM!

The dependency is correctly added with the same git revision as other reth crates, maintaining version consistency. It's required for the StateProvider trait used in the new BerachainBlockBuilder.

src/node/evm/builder.rs (4)

18-49: Well-documented struct with clear problem statement and solution.

The struct-level documentation thoroughly explains the sender/transaction mismatch problem, the solution approach, and the invariants enforced. This provides excellent context for future maintainers.


107-127: Clarify or enforce the single PoL injection invariant.

The comment on line 108 questions whether to assert num_injected == 1. If only a single PoL transaction is ever injected post-Prague1, consider enforcing this invariant with an explicit check:

🔎 Suggested enforcement
             // Post-Prague1 with mismatch = expected, fix it
             (true, true) => {
-                // There should only be 1 injected tx -> assert this?
                 let num_injected = num_txs - num_senders;
+                if num_injected != 1 {
+                    return Err(BlockExecutionError::msg(format!(
+                        "expected exactly 1 PoL injection post-Prague1, found {} at timestamp {}",
+                        num_injected, timestamp
+                    )));
+                }
                 let mut fixed_senders = Vec::with_capacity(num_txs);

Alternatively, if multiple PoL injections are valid, remove the comment to avoid confusion.


129-133: Sender reconstruction logic is correct.

The approach of extending the fixed senders with existing senders and reconstructing the block is sound. The clone_block() call is necessary since RecoveredBlock::new_unhashed takes ownership.


150-160: LGTM!

The executor accessor methods correctly delegate to the inner builder.

src/node/evm/config.rs (2)

7-7: LGTM!

The import of BerachainBlockBuilder is correctly added alongside BerachainReceiptBuilder.


20-20: LGTM!

The new imports support the create_block_builder method implementation.

Also applies to: 29-30

Comment on lines +247 to +269
fn create_block_builder<'a, DB, I>(
&'a self,
evm: EvmFor<Self, &'a mut State<DB>, I>,
parent: &'a SealedHeader<HeaderTy<Self::Primitives>>,
ctx: <Self::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>,
) -> impl reth_evm::execute::BlockBuilder<
Primitives = Self::Primitives,
Executor: reth_evm::block::BlockExecutorFor<'a, Self::BlockExecutorFactory, DB, I>,
>
where
DB: Database,
I: InspectorFor<Self, &'a mut State<DB>> + 'a,
{
let inner: BasicBlockBuilder<'a, Self, _, BerachainBlockAssembler, BerachainPrimitives> =
BasicBlockBuilder {
executor: BlockExecutorFactory::create_executor(self, evm, ctx.clone()),
ctx,
assembler: self.block_assembler().clone(),
parent,
transactions: Vec::new(),
};
BerachainBlockBuilder::new(inner, self.spec.clone())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, examine imports at the top of the file
head -50 src/node/evm/config.rs | grep -E "use|import|BasicBlockBuilder"

Repository: berachain/bera-reth

Length of output: 617


🏁 Script executed:

# Check where BasicBlockBuilder is imported from
rg "BasicBlockBuilder" src/node/evm/config.rs -B 5 -A 5

Repository: berachain/bera-reth

Length of output: 1041


🏁 Script executed:

# Search for BasicBlockBuilder definition in the repository
fd "*.rs" | xargs rg "struct BasicBlockBuilder|impl BasicBlockBuilder" -l

Repository: berachain/bera-reth

Length of output: 348


🏁 Script executed:

# Check if there's a constructor or factory method for BasicBlockBuilder
rg "impl.*BasicBlockBuilder" --type rs

Repository: berachain/bera-reth

Length of output: 89


🏁 Script executed:

rg "BasicBlockBuilder" src/ -A 3 -B 3

Repository: berachain/bera-reth

Length of output: 1668


🏁 Script executed:

# Also search for any factory methods or builder patterns in block-related code
rg "BlockBuilder|block_builder|create_block" src/node/evm/ -l

Repository: berachain/bera-reth

Length of output: 109


🏁 Script executed:

cat src/node/evm/builder.rs | head -100

Repository: berachain/bera-reth

Length of output: 3960


🏁 Script executed:

# Search for BerachainBlockBuilder definition
rg "struct BerachainBlockBuilder|impl BerachainBlockBuilder" src/ -A 10

Repository: berachain/bera-reth

Length of output: 577


🏁 Script executed:

sed -n '240,270p' src/node/evm/config.rs

Repository: berachain/bera-reth

Length of output: 1292


🏁 Script executed:

# Check broader context for any comments near the method
sed -n '235,275p' src/node/evm/config.rs

Repository: berachain/bera-reth

Length of output: 1787


Add documentation explaining the direct BasicBlockBuilder construction.

The method lacks documentation explaining why BasicBlockBuilder is directly constructed with struct initialization rather than using an alternative approach. Since the coding guideline requires succinct documentation focusing on 'why', add a doc comment clarifying the design rationale—for example, whether this explicit construction is necessary to configure specific fields or if it's the only available approach from the reth dependency.

🤖 Prompt for AI Agents
In src/node/evm/config.rs around lines 247 to 269, add a short doc comment above
create_block_builder explaining why BasicBlockBuilder is constructed directly
via struct literal instead of an alternative (e.g., no public constructor in
reth, need to set fields explicitly, or to inject specific
executor/assembler/ctx/parent/transactions), describing the design rationale and
any implications for future changes; keep it succinct (one or two sentences) and
reference the relevant dependency behavior (reth API) so readers know this is
intentional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants