Skip to content

Commit

Permalink
Handle wrong root hash unhappy flow separately (#13576)
Browse files Browse the repository at this point in the history
After the root hash computation `flushAndCheckCommitmentV3` switches
from handling the main happy flow, to handling the error flow when the
root hash doesn't match what's in the header.

In this PR, I am moving the handling of the unhappy flow to a separate
function, so that the main body of `flushAndCheckCommitmentV3` can
continue to handle the happy flow,

---------

Co-authored-by: antonis19 <antonis19@users.noreply.github.com>
  • Loading branch information
antonis19 and antonis19 authored Jan 27, 2025
1 parent 5ebd0ae commit 5f0350c
Showing 1 changed file with 53 additions and 47 deletions.
100 changes: 53 additions & 47 deletions eth/stagedsync/exec3.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ var (
)

const (
changesetSafeRange = 32 // Safety net for long-sync, keep last 32 changesets
changesetSafeRange = 32 // Safety net for long-sync, keep last 32 changesets
maxUnwindJumpAllowance = 1000 // Maximum number of blocks we are allowed to unwind
)

func NewProgress(prevOutputBlockNum, commitThreshold uint64, workersCount int, updateMetrics bool, logPrefix string, logger log.Logger) *Progress {
Expand Down Expand Up @@ -798,51 +799,7 @@ func dumpPlainStateDebug(tx kv.RwTx, doms *state2.SharedDomains) {
}
}

// flushAndCheckCommitmentV3 - does write state to db and then check commitment
func flushAndCheckCommitmentV3(ctx context.Context, header *types.Header, applyTx kv.RwTx, doms *state2.SharedDomains, cfg ExecuteBlockCfg, e *StageState, maxBlockNum uint64, parallel bool, logger log.Logger, u Unwinder, inMemExec bool) (bool, error) {

// E2 state root check was in another stage - means we did flush state even if state root will not match
// And Unwind expecting it
if !parallel {
if err := e.Update(applyTx, maxBlockNum); err != nil {
return false, err
}
if _, err := rawdb.IncrementStateVersion(applyTx); err != nil {
return false, fmt.Errorf("writing plain state version: %w", err)
}
}

if header == nil {
return false, errors.New("header is nil")
}

if dbg.DiscardCommitment() {
return true, nil
}
if doms.BlockNum() != header.Number.Uint64() {
panic(fmt.Errorf("%d != %d", doms.BlockNum(), header.Number.Uint64()))
}

rh, err := doms.ComputeCommitment(ctx, true, header.Number.Uint64(), e.LogPrefix())
if err != nil {
return false, fmt.Errorf("StateV3.Apply: %w", err)
}
if cfg.blockProduction {
header.Root = common.BytesToHash(rh)
return true, nil
}
if bytes.Equal(rh, header.Root.Bytes()) {
if !inMemExec {
if err := doms.Flush(ctx, applyTx); err != nil {
return false, err
}
if err = applyTx.(state2.HasAggTx).AggTx().(*state2.AggregatorRoTx).PruneCommitHistory(ctx, applyTx, nil); err != nil {
return false, err
}
}
return true, nil
}
logger.Error(fmt.Sprintf("[%s] Wrong trie root of block %d: %x, expected (from header): %x. Block hash: %x", e.LogPrefix(), header.Number.Uint64(), rh, header.Root.Bytes(), header.Hash()))
func handleIncorrectRootHashError(header *types.Header, applyTx kv.RwTx, cfg ExecuteBlockCfg, e *StageState, maxBlockNum uint64, logger log.Logger, u Unwinder) (bool, error) {
if cfg.badBlockHalt {
return false, errors.New("wrong trie root")
}
Expand All @@ -862,7 +819,7 @@ func flushAndCheckCommitmentV3(ctx context.Context, header *types.Header, applyT
minBlockNum = max(minBlockNum, unwindToLimit)

// Binary search, but not too deep
jump := cmp.InRange(1, 1000, (maxBlockNum-minBlockNum)/2)
jump := cmp.InRange(1, maxUnwindJumpAllowance, (maxBlockNum-minBlockNum)/2)
unwindTo := maxBlockNum - jump

// protect from too far unwind
Expand All @@ -882,6 +839,55 @@ func flushAndCheckCommitmentV3(ctx context.Context, header *types.Header, applyT
return false, nil
}

// flushAndCheckCommitmentV3 - does write state to db and then check commitment
func flushAndCheckCommitmentV3(ctx context.Context, header *types.Header, applyTx kv.RwTx, doms *state2.SharedDomains, cfg ExecuteBlockCfg, e *StageState, maxBlockNum uint64, parallel bool, logger log.Logger, u Unwinder, inMemExec bool) (bool, error) {

// E2 state root check was in another stage - means we did flush state even if state root will not match
// And Unwind expecting it
if !parallel {
if err := e.Update(applyTx, maxBlockNum); err != nil {
return false, err
}
if _, err := rawdb.IncrementStateVersion(applyTx); err != nil {
return false, fmt.Errorf("writing plain state version: %w", err)
}
}

if header == nil {
return false, errors.New("header is nil")
}

if dbg.DiscardCommitment() {
return true, nil
}
if doms.BlockNum() != header.Number.Uint64() {
panic(fmt.Errorf("%d != %d", doms.BlockNum(), header.Number.Uint64()))
}

computedRootHash, err := doms.ComputeCommitment(ctx, true, header.Number.Uint64(), e.LogPrefix())
if err != nil {
return false, fmt.Errorf("StateV3.Apply: %w", err)
}
if cfg.blockProduction {
header.Root = common.BytesToHash(computedRootHash)
return true, nil
}
if !bytes.Equal(computedRootHash, header.Root.Bytes()) {
logger.Error(fmt.Sprintf("[%s] Wrong trie root of block %d: %x, expected (from header): %x. Block hash: %x", e.LogPrefix(), header.Number.Uint64(), computedRootHash, header.Root.Bytes(), header.Hash()))
return handleIncorrectRootHashError(header, applyTx, cfg, e, maxBlockNum, logger, u)
}
if !inMemExec {
if err := doms.Flush(ctx, applyTx); err != nil {
return false, err
}
if err = applyTx.(state2.HasAggTx).AggTx().(*state2.AggregatorRoTx).PruneCommitHistory(ctx, applyTx, nil); err != nil {
return false, err
}
}
return true, nil

}

func blockWithSenders(ctx context.Context, db kv.RoDB, tx kv.Tx, blockReader services.BlockReader, blockNum uint64) (b *types.Block, err error) {
if tx == nil {
tx, err = db.BeginRo(ctx)
Expand Down

0 comments on commit 5f0350c

Please sign in to comment.