erigon seg integrity --check=CommitmentRoot reports a false recomputed root does not match verified root on commitment .kv files whose checkpoint sits on a complete block boundary and whose block has account changes. This gates the v3.6 snapshot-release automation (erigon-snapshots-automation) on all chains (sepolia, ethmainnet, hoodi). The published snapshots are correct — it is a false positive in the check's recompute.
Symptom
integrity error: recomputed root does not match verified root:
0xf236e7… != 0x646f53… : in v2.1-commitment.2624-2656.kv with startTxNum=1025000000, endTxNum=1037500000
Data is valid
For each failing file the check reaches the recompute only after verifying stored root == canonical block-header root (checkCommitmentRootViaFileData) and stored root == SeekCommitment file-loaded trie root (checkCommitmentRootViaSd). Both pass. Only checkCommitmentRootViaRecompute (db/integrity/commitment_integrity.go, TouchChangedKeysFromHistory(blockMinTxNum, info.txNum+1) + ComputeCommitment) diverges.
Trigger (precise)
Fails iff info.txNum == blockMaxTxNum (PartialBlock()==false, complete block) and the block has account churn.
- partial-block files: always pass.
- clean-boundary,
accountTouches==0 (chiado 160-168): pass.
- clean-boundary,
accountTouches>0 (sepolia 2624-2656, hoodi 272-276): fail.
Reproduced, step-independent, identical roots
- snap36-sepolia:
references=false, step 390625, v2.1-commitment.2624-2656.kv: recomputed 0xf236e7 ≠ stored 0x646f53.
- arb-dev
/erigon-data/sepolia: references=false, step 1562500, archive, v2.1-commitment.656-664.kv, same txn range 1025000000-1037500000: identical 0xf236e7 ≠ 0x646f53.
Ruled out (by experiment)
RootHash() side-effect on the restored trie; state-reader txnum (info.txNum vs endTxNum-1); touch-set completeness (full-range touch); empty/tombstone branches (honored on read; unfold errors on them, recompute returns a wrong root not an error); step-size / migration (identical at 1562500 and 390625; datadir built fresh, not rebased); file version (v1.1/v2.1 confounded with partial/clean — a v2.1 partial file passes).
Why prod doesn't hit it
Production builds commitment incrementally and reuses persisted memoized stateHash; it never re-folds these cells. The recompute invalidates and re-folds them, exposing a latent divergence. Must not be "fixed" by disabling memoization in the check or relaxing the check.
Leading lead
accountTouches==0 passes → the diverging quantity is an account leaf hash recomputed at a complete block boundary, most likely the account's storageRoot.
Next step
TRIE_TRACE_BLOCK=11105912 on v2.1-commitment.2624-2656.kv (sepolia) → the exact diverging cell. Untested axis: references=true datadir.
Reproduces on older build cb894784, so likely preexisting in the recompute/fold path (or the #21026 FilesOnlyStateReader check), surfaced now because v3.6 runs CommitmentRoot in automation and the finer step size makes clean-boundary files common.
erigon seg integrity --check=CommitmentRootreports a falserecomputed root does not match verified rooton commitment.kvfiles whose checkpoint sits on a complete block boundary and whose block has account changes. This gates the v3.6 snapshot-release automation (erigon-snapshots-automation) on all chains (sepolia, ethmainnet, hoodi). The published snapshots are correct — it is a false positive in the check's recompute.Symptom
Data is valid
For each failing file the check reaches the recompute only after verifying
stored root == canonical block-header root(checkCommitmentRootViaFileData) andstored root == SeekCommitment file-loaded trie root(checkCommitmentRootViaSd). Both pass. OnlycheckCommitmentRootViaRecompute(db/integrity/commitment_integrity.go,TouchChangedKeysFromHistory(blockMinTxNum, info.txNum+1)+ComputeCommitment) diverges.Trigger (precise)
Fails iff
info.txNum == blockMaxTxNum(PartialBlock()==false, complete block) and the block has account churn.accountTouches==0(chiado160-168): pass.accountTouches>0(sepolia2624-2656, hoodi272-276): fail.Reproduced, step-independent, identical roots
references=false, step390625,v2.1-commitment.2624-2656.kv: recomputed0xf236e7≠ stored0x646f53./erigon-data/sepolia:references=false, step1562500, archive,v2.1-commitment.656-664.kv, same txn range1025000000-1037500000: identical0xf236e7≠0x646f53.Ruled out (by experiment)
RootHash()side-effect on the restored trie; state-reader txnum (info.txNumvsendTxNum-1); touch-set completeness (full-range touch); empty/tombstone branches (honored on read; unfold errors on them, recompute returns a wrong root not an error); step-size / migration (identical at 1562500 and 390625; datadir built fresh, not rebased); file version (v1.1/v2.1 confounded with partial/clean — a v2.1 partial file passes).Why prod doesn't hit it
Production builds commitment incrementally and reuses persisted memoized
stateHash; it never re-folds these cells. The recompute invalidates and re-folds them, exposing a latent divergence. Must not be "fixed" by disabling memoization in the check or relaxing the check.Leading lead
accountTouches==0passes → the diverging quantity is an account leaf hash recomputed at a complete block boundary, most likely the account'sstorageRoot.Next step
TRIE_TRACE_BLOCK=11105912onv2.1-commitment.2624-2656.kv(sepolia) → the exact diverging cell. Untested axis:references=truedatadir.Reproduces on older build
cb894784, so likely preexisting in the recompute/fold path (or the #21026FilesOnlyStateReadercheck), surfaced now because v3.6 runsCommitmentRootin automation and the finer step size makes clean-boundary files common.