datahaven-xyz/datahaven · error · Error

InvalidBlockRootsRootMerkleProof

InvalidBlockRootsRootMerkleProof

Error message

InvalidBlockRootsRootMerkleProof

What it means

The `block_roots_branch` in the update fails to prove `block_roots_root` is part of the beacon state at `update.header.state_root`. This proof (via `process_checkpoint_update`, using the fork-dependent `block_roots` generalized index) is required so later execution-header updates can do ancestry proofs. A failed verification means the block-roots root or branch does not correspond to the update's beacon state.

Solutions

  1. Fetch `block_roots_root` and its branch from a beacon node at exactly `update.header.slot` with the correct `block_roots` generalized index.
  2. Ensure `block_roots_gindex_at_slot` in `T::ForkVersions` reflects the fork at the header slot.
  3. Regenerate the whole update from a single consistent beacon state snapshot rather than mixing fields from different slots.
  4. Verify chain identity (testnet vs mainnet beacon chain).

Example fix

// before
 let branch = state.get_branch("block_roots")?; // fetched at state.slot
// after
 let branch = consensus_client.get_block_roots_branch(header.slot)?; // same slot as header.state_root
Defensive patterns

Strategy: validation

Validate before calling

const ok = verifyMerkleBranch(update.blockRootsRoot, update.blockRootsBranch, subtreeIndex(blockRootsGindex), generalizedIndexLength(blockRootsGindex), update.header.stateRoot);
if (!ok) throw new Error('block roots proof invalid');

Try / catch

try { await submit(update); } catch (e) { if (e.includes('InvalidBlockRootsRootMerkleProof')) { refetchProofsAt(headerSlot); } else throw e; }

Prevention

When it happens

Trigger: Submitting an `Update` where `verify_merkle_branch(update.block_roots_root, update.block_roots_branch, ...)` against `update.header.state_root` fails — wrong branch, wrong root, or state_root/branch from different states.

Common situations: Relayer computed the block_roots root itself instead of reading the state's field; branch taken at a different slot than the header; fork-specific gindex mismatch; relaying updates across a hard fork boundary with outdated fork version config.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13). Data as JSON: /api/errors/3d4476032d88c7b0. Report an issue: GitHub.

Appendix: source

Thrown at operator/pallets/ethereum-client/src/lib.rs:285

                    &update.current_sync_committee_branch,
                    subtree_index(sync_committee_gindex),
                    generalized_index_length(sync_committee_gindex),
                    update.header.state_root
                ),
                Error::<T>::InvalidSyncCommitteeMerkleProof
            );

            let header_root: H256 = update
                .header
                .hash_tree_root()
                .map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;

            // This is used for ancestry proofs in ExecutionHeader updates. This verifies the
            // BeaconState: the beacon state root is the tree root; the `block_roots` hash is the
            // tree leaf.
            let block_roots_gindex =
                Self::block_roots_gindex_at_slot(update.header.slot, fork_versions);
            ensure!(
                verify_merkle_branch(
                    update.block_roots_root,
                    &update.block_roots_branch,
                    subtree_index(block_roots_gindex),
                    generalized_index_length(block_roots_gindex),
                    update.header.state_root
                ),
                Error::<T>::InvalidBlockRootsRootMerkleProof
            );

            let sync_committee_prepared: SyncCommitteePrepared = (&update.current_sync_committee)
                .try_into()
                .map_err(|_| <Error<T>>::BLSPreparePublicKeysFailed)?;
            <CurrentSyncCommittee<T>>::set(sync_committee_prepared);
            <NextSyncCommittee<T>>::kill();
            InitialCheckpointRoot::<T>::set(header_root);

            Self::store_validators_root(update.validators_root);

View on GitHub (pinned to edcb13dbbc)