Skip to content

Can't enable both parallel state root calculating and multiproof prewarming #17637

Description

@fynnss

Describe the bug

I noticed that when --engine.legacy-state-root is enabled, the cache prewarming for multiproof is entirely disabled.

This is due to the logic in insert_block_inner, where spawn_cache_exclusive() is used if use_state_root_task is true and trie_input.prefix_sets is empty.

let mut handle = if use_state_root_task {
            // ....

            // Use state root task only if prefix sets are empty, otherwise proof generation is too
            // expensive because it requires walking over the paths in the prefix set in every
            // proof.
            if trie_input.prefix_sets.is_empty() {
                self.payload_processor.spawn(
                    header,
                    txs,
                    provider_builder,
                    consistent_view,
                    trie_input,
                    &self.config,
                )
            } else {
                debug!(target: "engine::tree", block=?block_num_hash, "Disabling state root task due to non-empty prefix sets");
                use_state_root_task = false;
                self.payload_processor.spawn_cache_exclusive(header, txs, provider_builder)
            }
        } else {
            self.payload_processor.spawn_cache_exclusive(header, txs, provider_builder)
        };

In that case, to_multi_proof is always set to None, so no prewarming happens for multiproof targets.

// crates/engine/tree/src/tree/payload_processor/mod.rs:250
let prewarm_handle = self.spawn_caching_with(header, transactions, provider_builder, None);

This creates a performance issue: even though we're using the legacy state root calculation (which is already parallel in foreground, not backgroud task), the multiproof task could still benefit from cache prewarming — but it doesn't work under this logic.

BTW, if --engine.legacy-state-root is not set, and the parallel root calculation fails (e.g. due to mismatch), it falls back to serial calculation.

2025-07-28T09:25:58.325384Z  WARN engine::tree: State root task returned incorrect state root state_root=0xc3ce4734893ed87f062462c90db3d745994865be65ec2575a500a70d1b1cbd1f block_state_root=0xe0c8c6fe648270d3b2ab29f33d386d70070d6ff097dbc522868515012a2f89ad
2025-07-28T09:25:58.325389Z  WARN engine::tree: Failed to compute state root in parallel block=NumHash { number: 1000050, hash: 0xf30cdb4efe57d0980026a79775a8dda83cb05763d3a329a994a9882bd3d799fb } persisting_kind=NotPersisting

Is this behavior expected? This cause that i can't have both parallel state root and prewarm proofs at the same. So the questions are: (:TLDR)

  1. Why is prewarming proofs only enabled when use_state_root_task is true and prefix_sets is empty? What’s the reason behind this condition?
  2. Why does the parallel root calculation always fail and fall back to the serial path when using state root task?

Thanks! Any feedback or clarification would be greatly appreciated.

Steps to reproduce

  1. pipeline sync to 100w
RUST_LOG=info ./target/release/reth-bsc node \
    --chain=bsc \
    --datadir=./data_dir \
    --debug.tip=0x48917a17f56f1f1e67771be69f08f0de69e76bed3149433d74ef7bcdba296546
  1. live sync
RUST_LOG=info ./target/release/reth-bsc node \
    --chain=bsc \
    --datadir=./data_dir \
    --authrpc.addr "0.0.0.0" \
    --authrpc.port 8551 \
    --authrpc.jwtsecret ./data_dir/jwt.hex \
    --http \
    --engine.legacy-state-root \
    --http.addr "0.0.0.0" \
    --http.port 8545 \
    --metrics 0.0.0.0:6060 \
    --http.api "admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots" ' > reth-bsc.log 

Node logs


Platform(s)

Linux (x86)

Container Type

Not running in a container

What version/commit are you on?

v1.5.1

What database version are you on?

Current database version: 2
Local database version: 2

Which chain / network are you on?

bsc

What type of node are you running?

Full via --full flag

What prune config do you use, if any?

No response

If you've built Reth from source, provide the full command you used

cargo build --release

Code of Conduct

  • I agree to follow the Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugAn unexpected or incorrect behaviorS-needs-triageThis issue needs to be labelled

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions