Skip to content

testing_commitBlockV1 does not persist committed block state — every commit after the first fails #11979

@MysticRyuujin

Description

@MysticRyuujin

Summary

When testing_commitBlockV1 is called more than once against the same node, the
first call succeeds but every subsequent call fails, because committing a
block does not persist that block's state to the state backend. The next commit then
cannot scope the parent (just-committed) block's state.

  • Flat backendInvalidOperationException: Unable to gather snapshots for state StateId { BlockNumber = N, ... }
  • Pruning-trie (patricia) backendMissingTrieNodeException: Node ... is missing from the DB

This breaks cross-client conformance for the execution-apis testing_commitBlockV1
fixtures: go-ethereum passes all of them; Nethermind fails every case whose commit
runs after a prior commit on the same node.

Affected version

Where it comes from

#11593 changed testing_commitBlockV1 to produce the block with DoNotUpdateHead
and then commit via CommitAsMainChain (SuggestBlock(ForceDontSetAsMain) +
UpdateMainChain(..., wereProcessed: true)), bypassing the main BlockchainProcessor.
The state computed during the producer pass is therefore never persisted by the normal
processing path. The in-code comment acknowledges this is fragile:

// Must NOT set ReadOnlyChain — empirically, the producer pass under FlatDb
// only appends a snapshot bundle when the chain is not read-only; without
// it the next commit's BeginScope(parent) fails with "Unable to gather snapshots".

In practice the snapshot bundle for the committed block is still not available to the
next commit, so the symptom reproduces even with the flat backend correctly enabled.

Reproduction

Run the execution-apis rpc-compat fixtures for testing_commitBlockV1 against
Nethermind (hive). The fixtures issue one testing_commitBlockV1 each, in sequence,
against a shared node. Result: the first commit succeeds; all later commits fail.

Observed across three independent state-backend configurations (so it is not a
config issue):

State backend config Node starts? Result
Pruning trie, Pruning.Mode=None (archive) yes MissingTrieNodeException on commit #2+
Flat (FlatDb.Enabled=true, ImportFromPruningTrieState=true) yes Unable to gather snapshots on commit #2+
Flat (FlatDb.Enabled=true, fresh node) yes Unable to gather snapshots on commit #2+

(Note: the hive config key Init.UseMemDb cannot be set to false to get RocksDB —
its setter ignores the value and always sets DiagnosticMode = MemDb. Removing the
key is required to reach a RocksDB/flat backend at all.)

Failing request (commit #2, building on the previously committed head)

{"jsonrpc":"2.0","id":2,"method":"testing_commitBlockV1","params":[
  {"parentBeaconBlockRoot":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884365149a42212e8822",
   "prevRandao":"0x1111111111111111111111111111111111111111111111111111111111111111",
   "suggestedFeeRecipient":"0x0000000000000000000000000000000000000000",
   "timestamp":"0x1e6","withdrawals":[]},
  ["0x02f8..."], "0x746573745f6e616d65"]}

Response (flat backend)

-32603 Internal error
System.InvalidOperationException: Unable to gather snapshots for state
  StateId { BlockNumber = 46, StateRoot = 0x4c6709fca84ff2bcca8609b4a54cb96fc1c001862d450d334088cd5694cad374 }.
   at Nethermind.State.Flat.FlatDbManager.GatherReadOnlySnapshotBundle(StateId& baseBlock)  FlatDbManager.cs:295
   at Nethermind.State.Flat.FlatDbManager.GatherSnapshotBundle(StateId& baseBlock, Usage usage)  FlatDbManager.cs:236
   at Nethermind.State.Flat.ScopeProvider.FlatScopeProvider.BeginScope(BlockHeader baseBlock)  FlatScopeProvider.cs:32
   at Nethermind.State.WorldState.BeginScope(BlockHeader baseBlock)  WorldState.cs:234
   at Nethermind.Consensus.Processing.BranchProcessor.Process(...)  BranchProcessor.cs:70
   at Nethermind.Consensus.Processing.BlockchainProcessor.Process(...)  BlockchainProcessor.cs:437
   at Nethermind.Consensus.Processing.OneTimeChainProcessor.Process(...)  OneTimeChainProcessor.cs:31
   at Nethermind.Merge.Plugin.TestingRpcModule.ProduceBlock(...)  TestingRpcModule.cs:145
   at Nethermind.Merge.Plugin.TestingRpcModule.testing_commitBlockV1(...)  TestingRpcModule.cs:97

Response (pruning-trie backend)

-32000  Node A: P: H:0x4c6709fca84ff2bcca8609b4a54cb96fc1c001862d450d334088cd5694cad374 is missing from the DB
Nethermind.Trie.MissingTrieNodeException ...
   at Nethermind.Blockchain.BeaconBlockRoot.BeaconBlockRootHandler.BeaconRootsAccessList(...)
   at Nethermind.Merge.Plugin.TestingRpcModule.ProduceBlock(...)  TestingRpcModule.cs:145

In both cases the state being requested is the head produced by the previous
testing_commitBlockV1, confirming that commit did not persist its post-state.

Expected behavior

After testing_commitBlockV1 commits a block and advances the head, that block's
state must be persisted so a subsequent testing_commitBlockV1 (or any block
production) can scope it as the parent. go-ethereum does this and passes all
execution-apis testing_commitBlockV1 fixtures.

Suggested fix area

Nethermind.Merge.Plugin.TestingRpcModule (commit path / CommitAsMainChain) and
Nethermind.State.Flat.FlatDbManager — persist the committed block's snapshot bundle
(and the patricia state) on commit, rather than relying on the producer pass having
done so.

References

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions