From 801d5eabe5571e8968a18832867f680689290fca Mon Sep 17 00:00:00 2001 From: Potuz Date: Thu, 28 Jul 2022 13:46:50 -0300 Subject: [PATCH] Deal with nil LVH (#11127) * Deal with nil LVH After https://github.com/ethereum/execution-apis/pull/254 the EL may return `nil` for LVH. Our current implementation would treat this as a zero hash and thus mark all blocks since the merge block as INVALID * Apply suggestions from Terence Co-authored-by: terencechain Co-authored-by: terencechain Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/blockchain/execution_engine.go | 5 ++ .../blockchain/execution_engine_test.go | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/beacon-chain/blockchain/execution_engine.go b/beacon-chain/blockchain/execution_engine.go index b970c0a1500b..c499adfba6f8 100644 --- a/beacon-chain/blockchain/execution_engine.go +++ b/beacon-chain/blockchain/execution_engine.go @@ -23,6 +23,8 @@ import ( "go.opencensus.io/trace" ) +var defaultLatestValidHash = bytesutil.PadTo([]byte{0xff}, 32) + // notifyForkchoiceUpdateArg is the argument for the forkchoice update notification `notifyForkchoiceUpdate`. type notifyForkchoiceUpdateArg struct { headState state.BeaconState @@ -89,6 +91,9 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho case powchain.ErrInvalidPayloadStatus: newPayloadInvalidNodeCount.Inc() headRoot := arg.headRoot + if len(lastValidHash) == 0 { + lastValidHash = defaultLatestValidHash + } invalidRoots, err := s.ForkChoicer().SetOptimisticToInvalid(ctx, headRoot, bytesutil.ToBytes32(headBlk.ParentRoot()), bytesutil.ToBytes32(lastValidHash)) if err != nil { log.WithError(err).Error("Could not set head root to invalid") diff --git a/beacon-chain/blockchain/execution_engine_test.go b/beacon-chain/blockchain/execution_engine_test.go index 68cb33ddae07..1782b0c48891 100644 --- a/beacon-chain/blockchain/execution_engine_test.go +++ b/beacon-chain/blockchain/execution_engine_test.go @@ -344,6 +344,85 @@ func Test_NotifyForkchoiceUpdateRecursive_Protoarray(t *testing.T) { require.Equal(t, true, fcs.HasNode(bre)) } +func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) { + ctx := context.Background() + beaconDB := testDB.SetupDB(t) + + // Prepare blocks + ba := util.NewBeaconBlockBellatrix() + ba.Block.Body.ExecutionPayload.BlockNumber = 1 + wba := util.SaveBlock(t, ctx, beaconDB, ba) + bra, err := wba.Block().HashTreeRoot() + require.NoError(t, err) + + bb := util.NewBeaconBlockBellatrix() + bb.Block.Body.ExecutionPayload.BlockNumber = 2 + wbb := util.SaveBlock(t, ctx, beaconDB, bb) + brb, err := wbb.Block().HashTreeRoot() + require.NoError(t, err) + + bc := util.NewBeaconBlockBellatrix() + pc := [32]byte{'C'} + bc.Block.Body.ExecutionPayload.BlockHash = pc[:] + bc.Block.Body.ExecutionPayload.BlockNumber = 3 + wbc := util.SaveBlock(t, ctx, beaconDB, bc) + brc, err := wbc.Block().HashTreeRoot() + require.NoError(t, err) + + bd := util.NewBeaconBlockBellatrix() + pd := [32]byte{'D'} + bd.Block.Body.ExecutionPayload.BlockHash = pd[:] + bd.Block.Body.ExecutionPayload.BlockNumber = 4 + bd.Block.ParentRoot = brc[:] + wbd := util.SaveBlock(t, ctx, beaconDB, bd) + brd, err := wbd.Block().HashTreeRoot() + require.NoError(t, err) + + // Insert blocks into forkchoice + service := setupBeaconChain(t, beaconDB) + fcs := doublylinkedtree.New() + service.cfg.ForkChoiceStore = fcs + service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache() + + service.justifiedBalances.balances = []uint64{50, 100, 200} + require.NoError(t, err) + ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} + ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} + state, blkRoot, err := prepareForkchoiceState(ctx, 1, bra, [32]byte{}, [32]byte{'A'}, ojc, ofc) + require.NoError(t, err) + require.NoError(t, fcs.InsertNode(ctx, state, blkRoot)) + state, blkRoot, err = prepareForkchoiceState(ctx, 2, brb, bra, [32]byte{'B'}, ojc, ofc) + require.NoError(t, err) + require.NoError(t, fcs.InsertNode(ctx, state, blkRoot)) + state, blkRoot, err = prepareForkchoiceState(ctx, 3, brc, brb, [32]byte{'C'}, ojc, ofc) + require.NoError(t, err) + require.NoError(t, fcs.InsertNode(ctx, state, blkRoot)) + state, blkRoot, err = prepareForkchoiceState(ctx, 4, brd, brc, [32]byte{'D'}, ojc, ofc) + require.NoError(t, err) + require.NoError(t, fcs.InsertNode(ctx, state, blkRoot)) + + // Prepare Engine Mock to return invalid LVH = nil + service.cfg.ExecutionEngineCaller = &mockPOW.EngineClient{ErrForkchoiceUpdated: powchain.ErrInvalidPayloadStatus, OverrideValidHash: [32]byte{'C'}} + st, _ := util.DeterministicGenesisState(t, 1) + service.head = &head{ + state: st, + block: wba, + } + + require.NoError(t, beaconDB.SaveState(ctx, st, bra)) + require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bra)) + a := ¬ifyForkchoiceUpdateArg{ + headState: st, + headBlock: wbd.Block(), + headRoot: brd, + } + _, err = service.notifyForkchoiceUpdate(ctx, a) + require.Equal(t, true, IsInvalidBlock(err)) + require.Equal(t, brd, InvalidBlockRoot(err)) + require.Equal(t, brd, InvalidAncestorRoots(err)[0]) + require.Equal(t, 1, len(InvalidAncestorRoots(err))) +} + // // // A <- B <- C <- D