Skip to content

Hdiff fixes 2#16534

Merged
prestonvanloon merged 3 commits intohdiff-restart-supportfrom
hdiff-fixes2
Mar 19, 2026
Merged

Hdiff fixes 2#16534
prestonvanloon merged 3 commits intohdiff-restart-supportfrom
hdiff-fixes2

Conversation

@Inspector-Butters
Copy link
Contributor

@Inspector-Butters Inspector-Butters commented Mar 13, 2026

In 3 places, in order to see if db has a state, we're directly reading from the stateBucket in a bolt tx. This would cause problems when using state diffs.
So this PR moves those checks outside of the tx and calls the HasState() function which already accounts for everything.

due to a bug where these HasState() calls result in a panic because the stateDiffCache is not initialized before the calls, the call to initialize state diff was moved higher to ensure we have the cache before doing anything.

@Inspector-Butters Inspector-Butters mentioned this pull request Mar 16, 2026
prestonvanloon pushed a commit that referenced this pull request Mar 19, 2026
This PR addresses multiple issues in the hdiff code:

1. the function `hasStateUsingStateDiff()` that's called in
`db.HasState()` was returning true for any slot that fell on the hdiff
tree levels. This is wrong because those states might not actually be
saved. This PR fixes that by actually looking in the db to check.
2. add the call to `saveStateByDiff()` in `db.SaveState()` to actually
save the states in the hdiff tree. this was missed previously. the
reason for the if guard `s.stateDiffCache != nil` over this call is that
when syncing from genesis, the call to initialize state diff comes after
the call of `SaveState()` to save the genesis state. which would cause a
panic here. with this guard, that save goes to the legacy `stateBucket`.
but the state will later get saved in the hdiff tree when
`initializeStateDiff()` is called.
3. adds an early return to `getStateUsingStateDiff()` when
`computeLevel(slot)` returns -1.
4. moves the call to `initializeStateDiff()` higher when doing
checkpoint sync. specifically moves it before the call to `SaveState()`.
otherwise there is a bug: state diff tries to save that state in the
tree because it has offset 0 from the genesis call, then it would error
out because the checkpoint slot doesn't have the necessary parents in
the tree. (the offset isn't updated yet). this move makes it so the
offset is updated before saving the state. the `SaveState()` call is
also skipped after initializing state diff, because it's redundant.
~~**NOTE:** maybe if this pattern is good, we want to do the same in
genesis.go to avoid writing to the legacy `stateBucket` entirely.~~ This
is done in #16534
5. other small fixes.
Comment on lines 683 to 687
hasStateInDB := s.HasState(ctx, blockRoot)
return s.db.Update(func(tx *bolt.Tx) error {
hasStateInDB := tx.Bucket(stateBucket).Get(blockRoot[:]) != nil
if !(hasStateInDB || hasStateSummary) {
return errors.New("no state or state summary found with head block root")
}
Copy link
Member

Choose a reason for hiding this comment

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

Let's move this if statement outside of the write transaction too.

		if !(hasStateInDB || hasStateSummary) {
			return errors.New("no state or state summary found with head block root")
		}

@prestonvanloon prestonvanloon merged commit dcaca37 into hdiff-restart-support Mar 19, 2026
15 of 17 checks passed
@prestonvanloon prestonvanloon deleted the hdiff-fixes2 branch March 19, 2026 16:39
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.

2 participants