From 18fc17c9035f2731af9c0d5aed21ec7c4c2d41da Mon Sep 17 00:00:00 2001 From: Potuz Date: Thu, 9 Jun 2022 19:28:30 -0300 Subject: [PATCH] Forkchoice checkpoints (#10823) * double_tree_changes * protoarray changes * beacon-chain changes * spec tests and debug rpc fixes * more conflicts * more conflicts * Terence's review Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/blockchain/chain_info_test.go | 22 ++-- .../blockchain/execution_engine_test.go | 25 ++-- beacon-chain/blockchain/head.go | 16 ++- beacon-chain/blockchain/head_test.go | 8 +- beacon-chain/blockchain/mock_test.go | 2 +- beacon-chain/blockchain/new_slot.go | 5 +- beacon-chain/blockchain/new_slot_test.go | 2 +- beacon-chain/blockchain/pow_block_test.go | 4 +- .../blockchain/process_attestation_test.go | 17 +-- beacon-chain/blockchain/process_block.go | 15 ++- .../blockchain/process_block_helpers.go | 12 +- beacon-chain/blockchain/process_block_test.go | 70 +++++------ beacon-chain/blockchain/receive_block_test.go | 6 +- beacon-chain/blockchain/service.go | 14 ++- beacon-chain/blockchain/service_test.go | 10 +- .../doubly-linked-tree/ffg_update_test.go | 29 +++-- .../doubly-linked-tree/forkchoice.go | 67 +++++----- .../doubly-linked-tree/forkchoice_test.go | 36 +++--- .../doubly-linked-tree/no_vote_test.go | 21 ++-- .../doubly-linked-tree/proposer_boost_test.go | 38 +++--- .../forkchoice/doubly-linked-tree/store.go | 11 +- .../doubly-linked-tree/store_test.go | 18 ++- .../forkchoice/doubly-linked-tree/types.go | 6 +- .../unrealized_justification.go | 8 +- .../unrealized_justification_test.go | 41 ++++--- .../doubly-linked-tree/vote_test.go | 45 +++---- beacon-chain/forkchoice/interfaces.go | 10 +- .../forkchoice/protoarray/ffg_update_test.go | 33 ++--- .../forkchoice/protoarray/no_vote_test.go | 21 ++-- .../protoarray/proposer_boost_test.go | 38 +++--- beacon-chain/forkchoice/protoarray/store.go | 86 ++++++------- .../forkchoice/protoarray/store_test.go | 116 ++++++++++++------ beacon-chain/forkchoice/protoarray/types.go | 6 +- .../protoarray/unrealized_justification.go | 8 +- .../unrealized_justification_test.go | 43 +++---- .../forkchoice/protoarray/vote_test.go | 47 +++---- beacon-chain/forkchoice/types/BUILD.bazel | 2 + beacon-chain/forkchoice/types/types.go | 16 ++- beacon-chain/node/node.go | 4 +- .../rpc/prysm/v1alpha1/debug/forkchoice.go | 4 +- .../prysm/v1alpha1/debug/forkchoice_test.go | 6 +- .../shared/common/forkchoice/service.go | 2 +- 42 files changed, 558 insertions(+), 432 deletions(-) diff --git a/beacon-chain/blockchain/chain_info_test.go b/beacon-chain/blockchain/chain_info_test.go index 9c9a10528d..8a77928766 100644 --- a/beacon-chain/blockchain/chain_info_test.go +++ b/beacon-chain/blockchain/chain_info_test.go @@ -80,9 +80,9 @@ func TestHeadRoot_Nil(t *testing.T) { } func TestService_ForkChoiceStore(t *testing.T) { - c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}} + c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}} p := c.ForkChoiceStore() - require.Equal(t, 0, int(p.FinalizedEpoch())) + require.Equal(t, types.Epoch(0), p.FinalizedCheckpoint().Epoch) } func TestFinalizedCheckpt_CanRetrieve(t *testing.T) { @@ -327,7 +327,7 @@ func TestService_HeadGenesisValidatorsRoot(t *testing.T) { } func TestService_ChainHeads_ProtoArray(t *testing.T) { ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}} + c := &Service{cfg: &config{ForkChoiceStore: protoarray.New()}} state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -357,7 +357,7 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) { func TestService_ChainHeads_DoublyLinkedTree(t *testing.T) { ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}} + c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}} state, blkRoot, err := prepareForkchoiceState(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -452,7 +452,7 @@ func TestService_IsOptimistic_ProtoArray(t *testing.T) { params.OverrideBeaconConfig(cfg) ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{ForkChoiceStore: protoarray.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -472,7 +472,7 @@ func TestService_IsOptimistic_DoublyLinkedTree(t *testing.T) { params.OverrideBeaconConfig(cfg) ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -495,7 +495,7 @@ func TestService_IsOptimisticBeforeBellatrix(t *testing.T) { func TestService_IsOptimisticForRoot_ProtoArray(t *testing.T) { ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{ForkChoiceStore: protoarray.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -510,7 +510,7 @@ func TestService_IsOptimisticForRoot_ProtoArray(t *testing.T) { func TestService_IsOptimisticForRoot_DoublyLinkedTree(t *testing.T) { ctx := context.Background() - c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) @@ -526,7 +526,7 @@ func TestService_IsOptimisticForRoot_DoublyLinkedTree(t *testing.T) { func TestService_IsOptimisticForRoot_DB_ProtoArray(t *testing.T) { beaconDB := testDB.SetupDB(t) ctx := context.Background() - c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} c.head = &head{root: params.BeaconConfig().ZeroHash} b := util.NewBeaconBlock() b.Block.Slot = 10 @@ -591,7 +591,7 @@ func TestService_IsOptimisticForRoot_DB_ProtoArray(t *testing.T) { func TestService_IsOptimisticForRoot_DB_DoublyLinkedTree(t *testing.T) { beaconDB := testDB.SetupDB(t) ctx := context.Background() - c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: doublylinkedtree.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} c.head = &head{root: params.BeaconConfig().ZeroHash} b := util.NewBeaconBlock() b.Block.Slot = 10 @@ -655,7 +655,7 @@ func TestService_IsOptimisticForRoot_DB_DoublyLinkedTree(t *testing.T) { func TestService_IsOptimisticForRoot_DB_non_canonical(t *testing.T) { beaconDB := testDB.SetupDB(t) ctx := context.Background() - c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}} + c := &Service{cfg: &config{BeaconDB: beaconDB, ForkChoiceStore: doublylinkedtree.New()}, head: &head{slot: 101, root: [32]byte{'b'}}} c.head = &head{root: params.BeaconConfig().ZeroHash} b := util.NewBeaconBlock() b.Block.Slot = 10 diff --git a/beacon-chain/blockchain/execution_engine_test.go b/beacon-chain/blockchain/execution_engine_test.go index 49fa7e4425..92c77d9dc9 100644 --- a/beacon-chain/blockchain/execution_engine_test.go +++ b/beacon-chain/blockchain/execution_engine_test.go @@ -11,6 +11,7 @@ import ( testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" doublylinkedtree "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" bstate "github.com/prysmaticlabs/prysm/beacon-chain/state" @@ -43,7 +44,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) { require.NoError(t, err) require.NoError(t, beaconDB.SaveBlock(ctx, altairBlk)) require.NoError(t, beaconDB.SaveBlock(ctx, bellatrixBlk)) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -184,7 +185,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) { st, _ := util.DeterministicGenesisState(t, 1) require.NoError(t, beaconDB.SaveState(ctx, st, tt.finalizedRoot)) require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, tt.finalizedRoot)) - fc := ðpb.Checkpoint{Epoch: 1, Root: tt.finalizedRoot[:]} + fc := ðpb.Checkpoint{Epoch: 0, Root: tt.finalizedRoot[:]} service.store.SetFinalizedCheckptAndPayloadHash(fc, [32]byte{'a'}) service.store.SetJustifiedCheckptAndPayloadHash(fc, [32]byte{'b'}) arg := ¬ifyForkchoiceUpdateArg{ @@ -289,7 +290,7 @@ func Test_NotifyForkchoiceUpdateRecursive(t *testing.T) { require.NoError(t, beaconDB.SaveBlock(ctx, wbg)) // Insert blocks into forkchoice - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -326,7 +327,9 @@ func Test_NotifyForkchoiceUpdateRecursive(t *testing.T) { fcs.ProcessAttestation(ctx, []uint64{0}, brd, 1) fcs.ProcessAttestation(ctx, []uint64{1}, brf, 1) fcs.ProcessAttestation(ctx, []uint64{2}, brg, 1) - headRoot, err := fcs.Head(ctx, bra, []uint64{50, 100, 200}) + jc := &forkchoicetypes.Checkpoint{Epoch: 0, Root: bra} + require.NoError(t, fcs.UpdateJustifiedCheckpoint(jc)) + headRoot, err := fcs.Head(ctx, []uint64{50, 100, 200}) require.NoError(t, err) require.Equal(t, brg, headRoot) @@ -347,7 +350,7 @@ func Test_NotifyForkchoiceUpdateRecursive(t *testing.T) { _, err = service.notifyForkchoiceUpdate(ctx, a) require.ErrorIs(t, ErrInvalidPayload, err) // Ensure Head is D - headRoot, err = fcs.Head(ctx, bra, service.justifiedBalances.balances) + headRoot, err = fcs.Head(ctx, service.justifiedBalances.balances) require.NoError(t, err) require.Equal(t, brd, headRoot) @@ -364,7 +367,7 @@ func Test_NotifyNewPayload(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -582,7 +585,7 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) { params.OverrideBeaconConfig(cfg) ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -625,7 +628,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -827,7 +830,7 @@ func Test_UpdateLastValidatedCheckpoint(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) stateGen := stategen.New(beaconDB) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stateGen), @@ -927,7 +930,7 @@ func TestService_removeInvalidBlockAndState(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), } service, err := NewService(ctx, opts...) require.NoError(t, err) @@ -983,7 +986,7 @@ func TestService_getPayloadHash(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), } service, err := NewService(ctx, opts...) require.NoError(t, err) diff --git a/beacon-chain/blockchain/head.go b/beacon-chain/blockchain/head.go index e30803f445..17af3d5d0a 100644 --- a/beacon-chain/blockchain/head.go +++ b/beacon-chain/blockchain/head.go @@ -12,6 +12,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice" doublylinkedtree "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/beacon-chain/state" "github.com/prysmaticlabs/prysm/config/features" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" @@ -95,16 +96,23 @@ func (s *Service) updateHead(ctx context.Context, balances []uint64) ([32]byte, return [32]byte{}, err } if features.Get().EnableForkChoiceDoublyLinkedTree { - s.cfg.ForkChoiceStore = doublylinkedtree.New(j.Epoch, f.Epoch) + s.cfg.ForkChoiceStore = doublylinkedtree.New() } else { - s.cfg.ForkChoiceStore = protoarray.New(j.Epoch, f.Epoch) + s.cfg.ForkChoiceStore = protoarray.New() } if err := s.insertBlockToForkChoiceStore(ctx, jb.Block(), headStartRoot, st, f, j); err != nil { return [32]byte{}, err } } - - return s.cfg.ForkChoiceStore.Head(ctx, headStartRoot, balances) + jc := &forkchoicetypes.Checkpoint{Epoch: j.Epoch, Root: headStartRoot} + fc := &forkchoicetypes.Checkpoint{Epoch: f.Epoch, Root: s.ensureRootNotZeros(bytesutil.ToBytes32(f.Root))} + if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(jc); err != nil { + return [32]byte{}, err + } + if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(fc); err != nil { + return [32]byte{}, err + } + return s.cfg.ForkChoiceStore.Head(ctx, balances) } // This saves head info to the local service cache, it also saves the diff --git a/beacon-chain/blockchain/head_test.go b/beacon-chain/blockchain/head_test.go index 13c9e3cade..726de6e234 100644 --- a/beacon-chain/blockchain/head_test.go +++ b/beacon-chain/blockchain/head_test.go @@ -174,10 +174,8 @@ func TestUpdateHead_MissingJustifiedRoot(t *testing.T) { service.store.SetJustifiedCheckptAndPayloadHash(ðpb.Checkpoint{Root: r[:]}, [32]byte{'a'}) service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{}, [32]byte{'b'}) service.store.SetBestJustifiedCheckpt(ðpb.Checkpoint{}) - headRoot, err := service.updateHead(context.Background(), []uint64{}) + _, err = service.updateHead(context.Background(), []uint64{}) require.NoError(t, err) - st, _ := util.DeterministicGenesisState(t, 1) - require.NoError(t, service.saveHead(context.Background(), headRoot, wsb, st)) } func Test_notifyNewHeadEvent(t *testing.T) { @@ -613,7 +611,7 @@ func TestUpdateHead_noSavedChanges(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -630,7 +628,7 @@ func TestUpdateHead_noSavedChanges(t *testing.T) { require.NoError(t, beaconDB.SaveBlock(ctx, bellatrixBlk)) fcp := ðpb.Checkpoint{ Root: bellatrixBlkRoot[:], - Epoch: 1, + Epoch: 0, } service.store.SetFinalizedCheckptAndPayloadHash(fcp, [32]byte{'a'}) service.store.SetJustifiedCheckptAndPayloadHash(fcp, [32]byte{'b'}) diff --git a/beacon-chain/blockchain/mock_test.go b/beacon-chain/blockchain/mock_test.go index e9a43759c5..9a24dde035 100644 --- a/beacon-chain/blockchain/mock_test.go +++ b/beacon-chain/blockchain/mock_test.go @@ -13,7 +13,7 @@ import ( func testServiceOptsWithDB(t *testing.T) []Option { beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() return []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), diff --git a/beacon-chain/blockchain/new_slot.go b/beacon-chain/blockchain/new_slot.go index 7394b35fc5..631a112e5d 100644 --- a/beacon-chain/blockchain/new_slot.go +++ b/beacon-chain/blockchain/new_slot.go @@ -5,7 +5,9 @@ import ( "context" "github.com/pkg/errors" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/time/slots" ) @@ -68,7 +70,8 @@ func (s *Service) NewSlot(ctx context.Context, slot types.Slot) error { return err } s.store.SetJustifiedCheckptAndPayloadHash(bj, h) - if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(bj); err != nil { + if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: bj.Epoch, Root: bytesutil.ToBytes32(bj.Root)}); err != nil { return err } } diff --git a/beacon-chain/blockchain/new_slot_test.go b/beacon-chain/blockchain/new_slot_test.go index 5c43c81b25..2f8ff1f509 100644 --- a/beacon-chain/blockchain/new_slot_test.go +++ b/beacon-chain/blockchain/new_slot_test.go @@ -20,7 +20,7 @@ import ( func TestService_newSlot(t *testing.T) { beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), diff --git a/beacon-chain/blockchain/pow_block_test.go b/beacon-chain/blockchain/pow_block_test.go index cb16ac32ec..af8b46b8de 100644 --- a/beacon-chain/blockchain/pow_block_test.go +++ b/beacon-chain/blockchain/pow_block_test.go @@ -109,7 +109,7 @@ func Test_validateMergeBlock(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -152,7 +152,7 @@ func Test_validateMergeBlock(t *testing.T) { func Test_getBlkParentHashAndTD(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), diff --git a/beacon-chain/blockchain/process_attestation_test.go b/beacon-chain/blockchain/process_attestation_test.go index 16b4b559e7..04c7f6cc8d 100644 --- a/beacon-chain/blockchain/process_attestation_test.go +++ b/beacon-chain/blockchain/process_attestation_test.go @@ -9,6 +9,7 @@ import ( testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" doublylinkedtree "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/config/params" @@ -28,7 +29,7 @@ func TestStore_OnAttestation_ErrorConditions_ProtoArray(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), WithStateGen(stategen.New(beaconDB)), } service, err := NewService(ctx, opts...) @@ -140,7 +141,7 @@ func TestStore_OnAttestation_ErrorConditions_DoublyLinkedTree(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), - WithForkChoiceStore(doublylinkedtree.New(0, 0)), + WithForkChoiceStore(doublylinkedtree.New()), WithStateGen(stategen.New(beaconDB)), } service, err := NewService(ctx, opts...) @@ -250,7 +251,7 @@ func TestStore_OnAttestation_Ok_ProtoArray(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -278,7 +279,7 @@ func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -487,7 +488,7 @@ func TestVerifyFinalizedConsistency_InconsistentRoot_ProtoArray(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -522,7 +523,7 @@ func TestVerifyFinalizedConsistency_InconsistentRoot_DoublyLinkedTree(t *testing ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -610,7 +611,9 @@ func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) { require.NoError(t, err) require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot)) - _, err = service.cfg.ForkChoiceStore.Head(ctx, r32, []uint64{}) + jc := &forkchoicetypes.Checkpoint{Epoch: 0, Root: r32} + require.NoError(t, service.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(jc)) + _, err = service.cfg.ForkChoiceStore.Head(ctx, []uint64{}) require.NoError(t, err) err = service.VerifyFinalizedConsistency(context.Background(), r33[:]) require.NoError(t, err) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index d311508b4f..c196126de5 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -222,10 +222,12 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo } s.store.SetJustifiedCheckptAndPayloadHash(postState.CurrentJustifiedCheckpoint(), h) // Update Forkchoice checkpoints - if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(psj); err != nil { + if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: psj.Epoch, Root: bytesutil.ToBytes32(psj.Root)}); err != nil { return err } - if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(psf); err != nil { + if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: psf.Epoch, Root: bytesutil.ToBytes32(psf.Root)}); err != nil { return err } } @@ -441,8 +443,8 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac } } args := &forkchoicetypes.BlockAndCheckpoints{Block: b.Block(), - JustifiedEpoch: jCheckpoints[i].Epoch, - FinalizedEpoch: fCheckpoints[i].Epoch} + JustifiedCheckpoint: jCheckpoints[i], + FinalizedCheckpoint: fCheckpoints[i]} pendingNodes[len(blks)-i-1] = args s.saveInitSyncBlock(blockRoots[i], b) if err = s.handleBlockAfterBatchVerify(ctx, b, blockRoots[i], fCheckpoints[i], jCheckpoints[i]); err != nil { @@ -461,7 +463,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac } // Prune forkchoice store only if the new finalized checkpoint is higher // than the finalized checkpoint in forkchoice store. - if fCheckpoints[len(blks)-1].Epoch > s.cfg.ForkChoiceStore.FinalizedEpoch() { + if fCheckpoints[len(blks)-1].Epoch > s.cfg.ForkChoiceStore.FinalizedCheckpoint().Epoch { if err := s.cfg.ForkChoiceStore.Prune(ctx, s.ensureRootNotZeros(bytesutil.ToBytes32(fCheckpoints[len(blks)-1].Root))); err != nil { return errors.Wrap(err, "could not prune fork choice nodes") } @@ -543,7 +545,8 @@ func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed interf return err } s.store.SetFinalizedCheckptAndPayloadHash(fCheckpoint, h) - if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(fCheckpoint); err != nil { + if err := s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: fCheckpoint.Epoch, Root: bytesutil.ToBytes32(fCheckpoint.Root)}); err != nil { return err } } diff --git a/beacon-chain/blockchain/process_block_helpers.go b/beacon-chain/blockchain/process_block_helpers.go index acac444401..8b0e0b94a1 100644 --- a/beacon-chain/blockchain/process_block_helpers.go +++ b/beacon-chain/blockchain/process_block_helpers.go @@ -218,7 +218,8 @@ func (s *Service) updateJustified(ctx context.Context, state state.ReadOnlyBeaco } s.store.SetJustifiedCheckptAndPayloadHash(cpt, h) // Update forkchoice's justified checkpoint - if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(cpt); err != nil { + if err := s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: cpt.Epoch, Root: bytesutil.ToBytes32(cpt.Root)}); err != nil { return err } } @@ -244,7 +245,8 @@ func (s *Service) updateJustifiedInitSync(ctx context.Context, cp *ethpb.Checkpo return err } s.store.SetJustifiedCheckptAndPayloadHash(cp, h) - return s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(cp) + return s.cfg.ForkChoiceStore.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{ + Epoch: cp.Epoch, Root: bytesutil.ToBytes32(cp.Root)}) } func (s *Service) updateFinalized(ctx context.Context, cp *ethpb.Checkpoint) error { @@ -360,7 +362,7 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa return err } pendingNodes = append(pendingNodes, &forkchoicetypes.BlockAndCheckpoints{Block: blk, - JustifiedEpoch: jCheckpoint.Epoch, FinalizedEpoch: fCheckpoint.Epoch}) + JustifiedCheckpoint: jCheckpoint, FinalizedCheckpoint: fCheckpoint}) // As long as parent node is not in fork choice store, and parent node is in DB. root := bytesutil.ToBytes32(blk.ParentRoot()) for !s.cfg.ForkChoiceStore.HasNode(root) && s.cfg.BeaconDB.HasBlock(ctx, root) { @@ -373,8 +375,8 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk interfa } root = bytesutil.ToBytes32(b.Block().ParentRoot()) args := &forkchoicetypes.BlockAndCheckpoints{Block: b.Block(), - JustifiedEpoch: jCheckpoint.Epoch, - FinalizedEpoch: fCheckpoint.Epoch} + JustifiedCheckpoint: jCheckpoint, + FinalizedCheckpoint: fCheckpoint} pendingNodes = append(pendingNodes, args) } if len(pendingNodes) == 1 { diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 41b4940041..44d614f0bc 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -47,7 +47,7 @@ func TestStore_OnBlock_ProtoArray(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -150,7 +150,7 @@ func TestStore_OnBlock_DoublyLinkedTree(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -253,7 +253,7 @@ func TestStore_OnBlock_ProposerBoostEarly(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := doublylinkedtree.New(0, 0) + fcs := doublylinkedtree.New() opts := []Option{ WithStateGen(stategen.New(beaconDB)), WithForkChoiceStore(fcs), @@ -268,7 +268,7 @@ func TestStore_OnBlock_ProposerBoostEarly(t *testing.T) { SecondsIntoSlot: 0, } require.NoError(t, service.cfg.ForkChoiceStore.BoostProposerRoot(ctx, args)) - _, err = service.cfg.ForkChoiceStore.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{}) + _, err = service.cfg.ForkChoiceStore.Head(ctx, []uint64{}) require.ErrorContains(t, "could not apply proposer boost score: invalid proposer boost root", err) } @@ -293,7 +293,7 @@ func TestStore_OnBlockBatch_ProtoArray(t *testing.T) { service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'}) service.store.SetJustifiedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'b'}) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -340,7 +340,7 @@ func TestStore_OnBlockBatch_ProtoArray(t *testing.T) { require.NoError(t, err) jroot := bytesutil.ToBytes32(jcp.Root) require.Equal(t, blkRoots[63], jroot) - require.Equal(t, types.Epoch(2), service.cfg.ForkChoiceStore.JustifiedEpoch()) + require.Equal(t, types.Epoch(2), service.cfg.ForkChoiceStore.JustifiedCheckpoint().Epoch) } func TestStore_OnBlockBatch_PruneOK(t *testing.T) { @@ -362,7 +362,7 @@ func TestStore_OnBlockBatch_PruneOK(t *testing.T) { gRoot, err := genesis.Block.HashTreeRoot() require.NoError(t, err) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -427,7 +427,7 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) { service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'}) service.store.SetJustifiedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'b'}) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -474,7 +474,7 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) { require.NoError(t, err) jroot := bytesutil.ToBytes32(jcp.Root) require.Equal(t, blkRoots[63], jroot) - require.Equal(t, types.Epoch(2), service.cfg.ForkChoiceStore.JustifiedEpoch()) + require.Equal(t, types.Epoch(2), service.cfg.ForkChoiceStore.JustifiedCheckpoint().Epoch) } func TestStore_OnBlockBatch_NotifyNewPayload(t *testing.T) { @@ -496,7 +496,7 @@ func TestStore_OnBlockBatch_NotifyNewPayload(t *testing.T) { service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'}) service.store.SetJustifiedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{'b'}) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() service.saveInitSyncBlock(gRoot, wsb) st, keys := util.DeterministicGenesisState(t, 64) bState := st.Copy() @@ -577,7 +577,7 @@ func TestShouldUpdateJustified_ReturnFalse_ProtoArray(t *testing.T) { opts := testServiceOptsWithDB(t) service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() lastJustifiedBlk := util.NewBeaconBlock() lastJustifiedBlk.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, 32) lastJustifiedRoot, err := lastJustifiedBlk.Block.HashTreeRoot() @@ -610,7 +610,7 @@ func TestShouldUpdateJustified_ReturnFalse_DoublyLinkedTree(t *testing.T) { opts := testServiceOptsWithDB(t) service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() lastJustifiedBlk := util.NewBeaconBlock() lastJustifiedBlk.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, 32) lastJustifiedRoot, err := lastJustifiedBlk.Block.HashTreeRoot() @@ -657,7 +657,7 @@ func TestCachedPreState_CanGetFromStateSummary_ProtoArray(t *testing.T) { gRoot, err := genesis.Block.HashTreeRoot() require.NoError(t, err) service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{}) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -694,7 +694,7 @@ func TestCachedPreState_CanGetFromStateSummary_DoublyLinkedTree(t *testing.T) { gRoot, err := genesis.Block.HashTreeRoot() require.NoError(t, err) service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{}) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -728,7 +728,7 @@ func TestCachedPreState_CanGetFromDB(t *testing.T) { gRoot, err := genesis.Block.HashTreeRoot() require.NoError(t, err) service.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Root: gRoot[:]}, [32]byte{}) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() wsb, err = wrapper.WrappedSignedBeaconBlock(genesis) require.NoError(t, err) service.saveInitSyncBlock(gRoot, wsb) @@ -759,7 +759,7 @@ func TestUpdateJustified_CouldUpdateBest(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), } service, err := NewService(ctx, opts...) require.NoError(t, err) @@ -805,7 +805,7 @@ func TestFillForkChoiceMissingBlocks_CanSave_ProtoArray(t *testing.T) { } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -851,7 +851,7 @@ func TestFillForkChoiceMissingBlocks_CanSave_DoublyLinkedTree(t *testing.T) { } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -898,7 +898,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch_ProtoArray(t *testing.T) { } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -947,7 +947,7 @@ func TestFillForkChoiceMissingBlocks_RootsMatch_DoublyLinkedTree(t *testing.T) { } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -996,7 +996,7 @@ func TestFillForkChoiceMissingBlocks_FilterFinalized_ProtoArray(t *testing.T) { } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -1063,7 +1063,7 @@ func TestFillForkChoiceMissingBlocks_FilterFinalized_DoublyLinkedTree(t *testing } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -1131,7 +1131,7 @@ func TestFillForkChoiceMissingBlocks_FinalizedSibling_DoublyLinkedTree(t *testin } service, err := NewService(ctx, opts...) require.NoError(t, err) - service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0) + service.cfg.ForkChoiceStore = doublylinkedtree.New() genesisStateRoot := [32]byte{} genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) @@ -1272,7 +1272,7 @@ func TestAncestor_HandleSkipSlot(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -1363,7 +1363,7 @@ func TestAncestor_CanUseDB(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -1425,7 +1425,7 @@ func TestVerifyBlkDescendant(t *testing.T) { beaconDB := testDB.SetupDB(t) ctx := context.Background() - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -1570,7 +1570,7 @@ func TestHandleEpochBoundary_UpdateFirstSlot(t *testing.T) { func TestOnBlock_CanFinalize(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() depositCache, err := depositcache.New() require.NoError(t, err) opts := []Option{ @@ -1626,7 +1626,7 @@ func TestOnBlock_CanFinalize(t *testing.T) { func TestOnBlock_NilBlock(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() depositCache, err := depositcache.New() require.NoError(t, err) opts := []Option{ @@ -1645,7 +1645,7 @@ func TestOnBlock_NilBlock(t *testing.T) { func TestOnBlock_InvalidSignature(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() depositCache, err := depositcache.New() require.NoError(t, err) opts := []Option{ @@ -1686,7 +1686,7 @@ func TestOnBlock_CallNewPayloadAndForkchoiceUpdated(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() depositCache, err := depositcache.New() require.NoError(t, err) opts := []Option{ @@ -1924,7 +1924,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -2052,7 +2052,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) { func TestService_insertSlashingsToForkChoiceStore(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), @@ -2103,7 +2103,7 @@ func TestService_insertSlashingsToForkChoiceStore(t *testing.T) { func TestOnBlock_ProcessBlocksParallel(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() depositCache, err := depositcache.New() require.NoError(t, err) opts := []Option{ @@ -2175,7 +2175,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) { require.NoError(t, service.cfg.BeaconDB.DeleteBlock(ctx, r2)) require.NoError(t, service.cfg.BeaconDB.DeleteBlock(ctx, r3)) require.NoError(t, service.cfg.BeaconDB.DeleteBlock(ctx, r4)) - service.cfg.ForkChoiceStore = protoarray.New(0, 0) + service.cfg.ForkChoiceStore = protoarray.New() } } @@ -2183,7 +2183,7 @@ func Test_verifyBlkFinalizedSlot_invalidBlock(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) - fcs := protoarray.New(0, 0) + fcs := protoarray.New() opts := []Option{ WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), diff --git a/beacon-chain/blockchain/receive_block_test.go b/beacon-chain/blockchain/receive_block_test.go index 0c2e1bfb1b..08a103b51e 100644 --- a/beacon-chain/blockchain/receive_block_test.go +++ b/beacon-chain/blockchain/receive_block_test.go @@ -127,7 +127,7 @@ func TestService_ReceiveBlock(t *testing.T) { opts := []Option{ WithDatabase(beaconDB), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), WithAttestationPool(attestations.NewPool()), WithExitPool(voluntaryexits.NewPool()), WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}), @@ -168,7 +168,7 @@ func TestService_ReceiveBlockUpdateHead(t *testing.T) { require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot)) opts := []Option{ WithDatabase(beaconDB), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), WithAttestationPool(attestations.NewPool()), WithExitPool(voluntaryexits.NewPool()), WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}), @@ -248,7 +248,7 @@ func TestService_ReceiveBlockBatch(t *testing.T) { beaconDB := testDB.SetupDB(t) opts := []Option{ WithDatabase(beaconDB), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}), WithStateGen(stategen.New(beaconDB)), } diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 448ccd0879..7e7171120d 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -23,6 +23,7 @@ import ( f "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice" doublylinkedtree "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits" @@ -208,11 +209,20 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error { var forkChoicer f.ForkChoicer fRoot := s.ensureRootNotZeros(bytesutil.ToBytes32(finalized.Root)) if features.Get().EnableForkChoiceDoublyLinkedTree { - forkChoicer = doublylinkedtree.New(justified.Epoch, finalized.Epoch) + forkChoicer = doublylinkedtree.New() } else { - forkChoicer = protoarray.New(justified.Epoch, finalized.Epoch) + forkChoicer = protoarray.New() } s.cfg.ForkChoiceStore = forkChoicer + if err := forkChoicer.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: justified.Epoch, + Root: bytesutil.ToBytes32(justified.Root)}); err != nil { + return errors.Wrap(err, "could not update forkchoice's justified checkpoint") + } + if err := forkChoicer.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: finalized.Epoch, + Root: bytesutil.ToBytes32(finalized.Root)}); err != nil { + return errors.Wrap(err, "could not update forkchoice's finalized checkpoint") + } + st, err := s.cfg.StateGen.StateByRoot(s.ctx, fRoot) if err != nil { return errors.Wrap(err, "could not get finalized checkpoint state") diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index a0e992c7d3..e12db006c2 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -130,7 +130,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service { WithAttestationPool(attestations.NewPool()), WithP2PBroadcaster(&mockBroadcaster{}), WithStateNotifier(&mockBeaconNode{}), - WithForkChoiceStore(protoarray.New(0, 0)), + WithForkChoiceStore(protoarray.New()), WithAttestationService(attService), WithStateGen(stateGen), } @@ -505,7 +505,7 @@ func TestHasBlock_ForkChoiceAndDB_ProtoArray(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) s := &Service{ - cfg: &config{ForkChoiceStore: protoarray.New(0, 0), BeaconDB: beaconDB}, + cfg: &config{ForkChoiceStore: protoarray.New(), BeaconDB: beaconDB}, store: &store.Store{}, } s.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, [32]byte{}) @@ -526,7 +526,7 @@ func TestHasBlock_ForkChoiceAndDB_DoublyLinkedTree(t *testing.T) { ctx := context.Background() beaconDB := testDB.SetupDB(t) s := &Service{ - cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0), BeaconDB: beaconDB}, + cfg: &config{ForkChoiceStore: doublylinkedtree.New(), BeaconDB: beaconDB}, store: &store.Store{}, } s.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, [32]byte{}) @@ -599,7 +599,7 @@ func BenchmarkHasBlockForkChoiceStore_ProtoArray(b *testing.B) { ctx := context.Background() beaconDB := testDB.SetupDB(b) s := &Service{ - cfg: &config{ForkChoiceStore: protoarray.New(0, 0), BeaconDB: beaconDB}, + cfg: &config{ForkChoiceStore: protoarray.New(), BeaconDB: beaconDB}, store: &store.Store{}, } s.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, [32]byte{}) @@ -622,7 +622,7 @@ func BenchmarkHasBlockForkChoiceStore_DoublyLinkedTree(b *testing.B) { ctx := context.Background() beaconDB := testDB.SetupDB(b) s := &Service{ - cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0), BeaconDB: beaconDB}, + cfg: &config{ForkChoiceStore: doublylinkedtree.New(), BeaconDB: beaconDB}, store: &store.Store{}, } s.store.SetFinalizedCheckptAndPayloadHash(ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, [32]byte{}) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/ffg_update_test.go b/beacon-chain/forkchoice/doubly-linked-tree/ffg_update_test.go index a470dfcacf..4d1bff8da4 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/ffg_update_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/ffg_update_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" "github.com/prysmaticlabs/prysm/testing/assert" @@ -16,7 +17,7 @@ func TestFFGUpdates_OneBranch(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -46,7 +47,7 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 // | // 3 <- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head for with justified epoch at 0") @@ -58,8 +59,8 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 <- head // | // 3 - f.store.justifiedEpoch = 1 - r, err = f.Head(context.Background(), indexToHash(2), balances) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Root: indexToHash(2), Epoch: 1} + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head with justified epoch at 1") @@ -71,8 +72,8 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 <- start // | // 3 <- head - f.store.justifiedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(3), balances) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Root: indexToHash(3), Epoch: 2} + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head with justified epoch at 2") } @@ -82,7 +83,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { f := setup(0, 0) ctx := context.Background() - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -143,7 +144,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // 9 10 <-- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head with justified epoch at 0") @@ -173,7 +174,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // head -> 9 10 - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head with justified epoch at 0") @@ -203,19 +204,21 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // 9 10 <-- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head with justified epoch at 0") - f.store.justifiedEpoch = 1 - r, err = f.Head(context.Background(), indexToHash(1), balances) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1, Root: indexToHash(1)} + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(7), r, "Incorrect head with justified epoch at 0") } func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { ctx := context.Background() - f := New(justifiedEpoch, finalizedEpoch) + f := New() + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: justifiedEpoch, Root: params.BeaconConfig().ZeroHash} + f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: finalizedEpoch, Root: params.BeaconConfig().ZeroHash} state, blkRoot, err := prepareForkchoiceState(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, params.BeaconConfig().ZeroHash, justifiedEpoch, finalizedEpoch) if err != nil { return nil diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go index 6556622c13..720024a18d 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go @@ -13,22 +13,22 @@ import ( "github.com/prysmaticlabs/prysm/config/params" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" "github.com/prysmaticlabs/prysm/encoding/bytesutil" - pbrpc "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/runtime/version" "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) // New initializes a new fork choice store. -func New(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { +func New() *ForkChoice { s := &Store{ - justifiedEpoch: justifiedEpoch, - finalizedEpoch: finalizedEpoch, - proposerBoostRoot: [32]byte{}, - nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node), - nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node), - slashedIndices: make(map[types.ValidatorIndex]bool), - pruneThreshold: defaultPruneThreshold, + justifiedCheckpoint: &forkchoicetypes.Checkpoint{}, + finalizedCheckpoint: &forkchoicetypes.Checkpoint{}, + proposerBoostRoot: [32]byte{}, + nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node), + nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node), + slashedIndices: make(map[types.ValidatorIndex]bool), + pruneThreshold: defaultPruneThreshold, } b := make([]uint64, 0) @@ -47,7 +47,6 @@ func (f *ForkChoice) NodeCount() int { // It firsts computes validator's balance changes then recalculates block tree from leaves to root. func (f *ForkChoice) Head( ctx context.Context, - justifiedRoot [32]byte, justifiedStateBalances []uint64, ) ([32]byte, error) { ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.Head") @@ -73,10 +72,12 @@ func (f *ForkChoice) Head( return [32]byte{}, errors.Wrap(err, "could not apply weight changes") } - if err := f.store.treeRootNode.updateBestDescendant(ctx, f.store.justifiedEpoch, f.store.finalizedEpoch); err != nil { + jc := f.JustifiedCheckpoint() + fc := f.FinalizedCheckpoint() + if err := f.store.treeRootNode.updateBestDescendant(ctx, jc.Epoch, fc.Epoch); err != nil { return [32]byte{}, errors.Wrap(err, "could not update best descendant") } - return f.store.head(ctx, justifiedRoot) + return f.store.head(ctx) } // ProcessAttestation processes attestation for vote accounting, it iterates around validator indices @@ -326,20 +327,24 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams return node.setNodeAndParentValidated(ctx) } -// JustifiedEpoch of fork choice store. -func (f *ForkChoice) JustifiedEpoch() types.Epoch { - return f.store.justifiedEpoch +// JustifiedCheckpoint of fork choice store. +func (f *ForkChoice) JustifiedCheckpoint() *forkchoicetypes.Checkpoint { + f.store.checkpointsLock.RLock() + defer f.store.checkpointsLock.RUnlock() + return f.store.justifiedCheckpoint } -// FinalizedEpoch of fork choice store. -func (f *ForkChoice) FinalizedEpoch() types.Epoch { - return f.store.finalizedEpoch +// FinalizedCheckpoint of fork choice store. +func (f *ForkChoice) FinalizedCheckpoint() *forkchoicetypes.Checkpoint { + f.store.checkpointsLock.RLock() + defer f.store.checkpointsLock.RUnlock() + return f.store.finalizedCheckpoint } -func (f *ForkChoice) ForkChoiceNodes() []*pbrpc.ForkChoiceNode { +func (f *ForkChoice) ForkChoiceNodes() []*ethpb.ForkChoiceNode { f.store.nodesLock.RLock() defer f.store.nodesLock.RUnlock() - ret := make([]*pbrpc.ForkChoiceNode, len(f.store.nodeByRoot)) + ret := make([]*ethpb.ForkChoiceNode, len(f.store.nodeByRoot)) return f.store.treeRootNode.rpcNodes(ret) } @@ -384,25 +389,25 @@ func (f *ForkChoice) InsertSlashedIndex(_ context.Context, index types.Validator } } -// UpdateJustifiedCheckpoint sets the justified epoch to the given one -func (f *ForkChoice) UpdateJustifiedCheckpoint(jc *pbrpc.Checkpoint) error { +// UpdateJustifiedCheckpoint sets the justified checkpoint to the given one +func (f *ForkChoice) UpdateJustifiedCheckpoint(jc *forkchoicetypes.Checkpoint) error { if jc == nil { return errInvalidNilCheckpoint } - f.store.nodesLock.Lock() - defer f.store.nodesLock.Unlock() - f.store.justifiedEpoch = jc.Epoch + f.store.checkpointsLock.Lock() + defer f.store.checkpointsLock.Unlock() + f.store.justifiedCheckpoint = jc return nil } -// UpdateFinalizedCheckpoint sets the finalized epoch to the given one -func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *pbrpc.Checkpoint) error { +// UpdateFinalizedCheckpoint sets the finalized checkpoint to the given one +func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *forkchoicetypes.Checkpoint) error { if fc == nil { return errInvalidNilCheckpoint } - f.store.nodesLock.Lock() - defer f.store.nodesLock.Unlock() - f.store.finalizedEpoch = fc.Epoch + f.store.checkpointsLock.Lock() + defer f.store.checkpointsLock.Unlock() + f.store.finalizedCheckpoint = fc return nil } @@ -473,7 +478,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho } if err := f.store.insert(ctx, b.Slot(), r, parentRoot, payloadHash, - chain[i].JustifiedEpoch, chain[i].FinalizedEpoch); err != nil { + chain[i].JustifiedCheckpoint.Epoch, chain[i].FinalizedCheckpoint.Epoch); err != nil { return err } } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go index 0f9e1d7c3e..b4124f011a 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go @@ -211,7 +211,9 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) { require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root) f.store.nodesLock.Unlock() - h, err := f.store.head(ctx, [32]byte{'1'}) + r1 := [32]byte{'1'} + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1, Root: r1} + h, err := f.store.head(ctx) require.NoError(t, err) require.DeepEqual(t, [32]byte{'3'}, h) require.DeepEqual(t, h, f.store.headNode.root) @@ -296,7 +298,7 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) { state, blkRoot, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - head, err := f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{}) + head, err := f.Head(ctx, []uint64{}) require.NoError(t, err) require.Equal(t, [32]byte{'a'}, head) @@ -307,21 +309,21 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{}) + head, err = f.Head(ctx, []uint64{}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) // Insert two attestations for block b, one for c it becomes head f.ProcessAttestation(ctx, []uint64{1, 2}, [32]byte{'b'}, 1) f.ProcessAttestation(ctx, []uint64{3}, [32]byte{'c'}, 1) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.NoError(t, err) require.Equal(t, [32]byte{'b'}, head) // Process b's slashing, c is now head f.InsertSlashedIndex(ctx, 1) require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight) require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight) require.NoError(t, err) @@ -330,7 +332,7 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) { // Process b's slashing again, should be a noop f.InsertSlashedIndex(ctx, 1) require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight) require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight) require.NoError(t, err) @@ -352,12 +354,14 @@ func TestStore_UpdateCheckpoints(t *testing.T) { f := setup(1, 1) jr := [32]byte{'j'} fr := [32]byte{'f'} - jc := ðpb.Checkpoint{Root: jr[:], Epoch: 3} - fc := ðpb.Checkpoint{Root: fr[:], Epoch: 2} + jc := &forkchoicetypes.Checkpoint{Root: jr, Epoch: 3} + fc := &forkchoicetypes.Checkpoint{Root: fr, Epoch: 2} require.NoError(t, f.UpdateJustifiedCheckpoint(jc)) require.NoError(t, f.UpdateFinalizedCheckpoint(fc)) - require.Equal(t, f.store.justifiedEpoch, jc.Epoch) - require.Equal(t, f.store.finalizedEpoch, fc.Epoch) + require.Equal(t, f.store.justifiedCheckpoint.Epoch, jc.Epoch) + require.Equal(t, f.store.justifiedCheckpoint.Root, jc.Root) + require.Equal(t, f.store.finalizedCheckpoint.Epoch, fc.Epoch) + require.Equal(t, f.store.finalizedCheckpoint.Root, fc.Root) } func TestStore_CommonAncestor(t *testing.T) { @@ -562,8 +566,10 @@ func TestStore_InsertOptimisticChain(t *testing.T) { require.NoError(t, err) wsb, err := wrapper.WrappedSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), JustifiedEpoch: 1, - FinalizedEpoch: 1}) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + }) for i := uint64(2); i < 11; i++ { blk := util.NewBeaconBlock() blk.Block.Slot = types.Slot(i) @@ -571,8 +577,10 @@ func TestStore_InsertOptimisticChain(t *testing.T) { blk.Block.ParentRoot = copiedRoot[:] wsb, err = wrapper.WrappedSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), JustifiedEpoch: 1, - FinalizedEpoch: 1}) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + }) root, err = blk.Block.HashTreeRoot() require.NoError(t, err) } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/no_vote_test.go b/beacon-chain/forkchoice/doubly-linked-tree/no_vote_test.go index 934c2e6061..763e40cd1c 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/no_vote_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/no_vote_test.go @@ -15,7 +15,7 @@ func TestNoVote_CanFindHead(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) if r != params.BeaconConfig().ZeroHash { t.Errorf("Incorrect head with genesis") @@ -28,7 +28,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err := prepareForkchoiceState(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -39,7 +39,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -52,7 +52,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -65,7 +65,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -80,7 +80,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -92,7 +92,8 @@ func TestNoVote_CanFindHead(t *testing.T) { // head -> 4 3 // | // 5 <- starting from 5 with justified epoch 0 should error - _, err = f.Head(context.Background(), indexToHash(5), balances) + f.store.justifiedCheckpoint.Root = indexToHash(5) + _, err = f.Head(context.Background(), balances) wanted := "head at slot 0 with weight 0 is not eligible, finalizedEpoch 1 != 1, justifiedEpoch 2 != 1" require.ErrorContains(t, wanted, err) @@ -104,8 +105,8 @@ func TestNoVote_CanFindHead(t *testing.T) { // 4 3 // | // 5 <- head - f.store.justifiedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(5), balances) + f.store.justifiedCheckpoint.Epoch = 2 + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(5), r, "Incorrect head for with justified epoch at 2") @@ -122,7 +123,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2") } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go index 015b620f09..e92362479f 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go @@ -35,7 +35,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - headRoot, err := f.Head(ctx, zeroHash, balances) + headRoot, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, headRoot, "Incorrect head with genesis") @@ -58,7 +58,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{0}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 1") @@ -82,7 +82,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{1}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 2") @@ -108,7 +108,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{2}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -144,7 +144,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { } require.NoError(t, f.BoostProposerRoot(ctx, args)) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -186,7 +186,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // Regression: process attestations for C, check that it // becomes head, we need two attestations to have C.weight = 30 > 24 = D.weight f.ProcessAttestation(ctx, []uint64{4, 5}, indexToHash(3), fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), headRoot, "Incorrect head for justified epoch at slot 4") @@ -195,7 +195,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -220,7 +220,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -239,7 +239,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure the head is C, the honest block. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -260,7 +260,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f.ProcessAttestation(ctx, votes, honestBlock, fEpoch) // Ensure the head is STILL C, the honest block, as the honest block had proposer boost. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") }) @@ -268,7 +268,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -295,7 +295,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -314,7 +314,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is still the head after the malicious proposer reveals their block. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -333,7 +333,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fEpoch) // Expect the head to have switched to B. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, maliciouslyWithheldBlock, r, "Expected B to become the head") }) @@ -355,7 +355,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { a := zeroHash // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -374,7 +374,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Incorrect head for justified epoch at slot 2") @@ -402,7 +402,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is still the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Incorrect head for justified epoch at slot 2") @@ -426,7 +426,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // D cannot win without a boost. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Expected C to remain the head") @@ -442,7 +442,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.BoostProposerRoot(ctx, args)) // Ensure D becomes the head thanks to boosting. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, d, r, "Expected D to become the head") }) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store.go b/beacon-chain/forkchoice/doubly-linked-tree/store.go index 495e314687..5a4169950e 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store.go @@ -60,12 +60,12 @@ func (s *Store) PruneThreshold() uint64 { // head starts from justified root and then follows the best descendant links // to find the best block for head. This function assumes a lock on s.nodesLock -func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, error) { +func (s *Store) head(ctx context.Context) ([32]byte, error) { _, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.head") defer span.End() // JustifiedRoot has to be known - justifiedNode, ok := s.nodeByRoot[justifiedRoot] + justifiedNode, ok := s.nodeByRoot[s.justifiedCheckpoint.Root] if !ok || justifiedNode == nil { return [32]byte{}, errUnknownJustifiedRoot } @@ -77,9 +77,9 @@ func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, err bestDescendant = justifiedNode } - if !bestDescendant.viableForHead(s.justifiedEpoch, s.finalizedEpoch) { + if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch) { return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch %d != %d, justifiedEpoch %d != %d", - bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, s.finalizedEpoch, bestDescendant.justifiedEpoch, s.justifiedEpoch) + bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, s.finalizedCheckpoint.Epoch, bestDescendant.justifiedEpoch, s.justifiedCheckpoint.Epoch) } // Update metrics. @@ -134,7 +134,8 @@ func (s *Store) insert(ctx context.Context, } } else { parent.children = append(parent.children, n) - if err := s.treeRootNode.updateBestDescendant(ctx, s.justifiedEpoch, s.finalizedEpoch); err != nil { + if err := s.treeRootNode.updateBestDescendant(ctx, + s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch); err != nil { return err } } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go index c09de38b06..1219c31056 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" "github.com/prysmaticlabs/prysm/testing/assert" @@ -22,13 +23,13 @@ func TestStore_PruneThreshold(t *testing.T) { func TestStore_JustifiedEpoch(t *testing.T) { j := types.Epoch(100) f := setup(j, j) - require.Equal(t, j, f.JustifiedEpoch()) + require.Equal(t, j, f.JustifiedCheckpoint().Epoch) } func TestStore_FinalizedEpoch(t *testing.T) { j := types.Epoch(50) f := setup(j, j) - require.Equal(t, j, f.FinalizedEpoch()) + require.Equal(t, j, f.FinalizedCheckpoint().Epoch) } func TestStore_NodeCount(t *testing.T) { @@ -78,7 +79,8 @@ func TestForkChoice_HasNode(t *testing.T) { func TestStore_Head_UnknownJustifiedRoot(t *testing.T) { f := setup(0, 0) - _, err := f.store.head(context.Background(), [32]byte{'a'}) + f.store.justifiedCheckpoint.Root = [32]byte{'a'} + _, err := f.store.head(context.Background()) assert.ErrorContains(t, errUnknownJustifiedRoot.Error(), err) } @@ -90,7 +92,8 @@ func TestStore_Head_Itself(t *testing.T) { // Since the justified node does not have a best descendant so the best node // is itself. - h, err := f.store.head(context.Background(), indexToHash(1)) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: indexToHash(1)} + h, err := f.store.head(context.Background()) require.NoError(t, err) assert.Equal(t, indexToHash(1), h) } @@ -110,7 +113,8 @@ func TestStore_Head_BestDescendant(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - h, err := f.store.head(context.Background(), indexToHash(1)) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: indexToHash(1)} + h, err := f.store.head(context.Background()) require.NoError(t, err) require.Equal(t, h, indexToHash(4)) } @@ -133,7 +137,9 @@ func TestStore_Insert(t *testing.T) { treeRootNode := &Node{slot: 0, root: indexToHash(0)} nodeByRoot := map[[32]byte]*Node{indexToHash(0): treeRootNode} nodeByPayload := map[[32]byte]*Node{indexToHash(0): treeRootNode} - s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode, nodeByPayload: nodeByPayload} + jc := &forkchoicetypes.Checkpoint{Epoch: 0} + fc := &forkchoicetypes.Checkpoint{Epoch: 0} + s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode, nodeByPayload: nodeByPayload, justifiedCheckpoint: jc, finalizedCheckpoint: fc} payloadHash := [32]byte{'a'} require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), payloadHash, 1, 1)) assert.Equal(t, 2, len(s.nodeByRoot), "Did not insert block") diff --git a/beacon-chain/forkchoice/doubly-linked-tree/types.go b/beacon-chain/forkchoice/doubly-linked-tree/types.go index dd28e59675..a674e7faf7 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/types.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/types.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "sync" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" ) @@ -17,8 +18,8 @@ type ForkChoice struct { // Store defines the fork choice store which includes block nodes and the last view of checkpoint information. type Store struct { - justifiedEpoch types.Epoch // latest justified epoch in store. - finalizedEpoch types.Epoch // latest finalized epoch in store. + justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified epoch in store. + finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized epoch in store. pruneThreshold uint64 // do not prune tree unless threshold is reached. proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner. previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner. @@ -30,6 +31,7 @@ type Store struct { slashedIndices map[types.ValidatorIndex]bool // the list of equivocating validator indices nodesLock sync.RWMutex proposerBoostLock sync.RWMutex + checkpointsLock sync.RWMutex } // Node defines the individual block which includes its block parent, ancestor and how much weight accounted for it. diff --git a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go index 18435aedf9..d3febe8238 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go @@ -44,11 +44,11 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() { for _, node := range f.store.nodeByRoot { node.justifiedEpoch = node.unrealizedJustifiedEpoch node.finalizedEpoch = node.unrealizedFinalizedEpoch - if node.justifiedEpoch > f.store.justifiedEpoch { - f.store.justifiedEpoch = node.justifiedEpoch + if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch { + f.store.justifiedCheckpoint.Epoch = node.justifiedEpoch } - if node.finalizedEpoch > f.store.finalizedEpoch { - f.store.finalizedEpoch = node.finalizedEpoch + if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch { + f.store.finalizedCheckpoint.Epoch = node.finalizedEpoch } } } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go index 9be0d9ffcd..620dddeb04 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/require" ) @@ -80,17 +80,17 @@ func TestStore_LongFork(t *testing.T) { // Add an attestation to c, it is head f.ProcessAttestation(ctx, []uint64{0}, [32]byte{'c'}, 1) - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, headRoot) // D is head even though its weight is lower. - hr := [32]byte{'d'} - state, blkRoot, err = prepareForkchoiceState(ctx, 103, hr, [32]byte{'b'}, [32]byte{'D'}, 2, 1) + ha := [32]byte{'a'} + state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'b'}, [32]byte{'D'}, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - require.NoError(t, f.UpdateJustifiedCheckpoint(ðpb.Checkpoint{Epoch: 2, Root: hr[:]})) - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + require.NoError(t, f.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 2, Root: ha})) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'d'}, headRoot) require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight) @@ -98,7 +98,7 @@ func TestStore_LongFork(t *testing.T) { // Update unrealized justification, c becomes head f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, headRoot) } @@ -157,30 +157,31 @@ func TestStore_NoDeadLock(t *testing.T) { // Epoch 3 // Current Head is H - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(0), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(0), f.JustifiedCheckpoint().Epoch) // Insert Block I, it becomes Head hr := [32]byte{'i'} state, blkRoot, err = prepareForkchoiceState(ctx, 108, hr, [32]byte{'f'}, [32]byte{'I'}, 1, 0) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - require.NoError(t, f.UpdateJustifiedCheckpoint(ðpb.Checkpoint{Epoch: 1, Root: hr[:]})) - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + ha := [32]byte{'a'} + require.NoError(t, f.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 1, Root: ha})) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'i'}, headRoot) - require.Equal(t, types.Epoch(1), f.JustifiedEpoch()) - require.Equal(t, types.Epoch(0), f.FinalizedEpoch()) + require.Equal(t, types.Epoch(1), f.JustifiedCheckpoint().Epoch) + require.Equal(t, types.Epoch(0), f.FinalizedCheckpoint().Epoch) // Realized Justified checkpoints, H becomes head f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(2), f.JustifiedEpoch()) - require.Equal(t, types.Epoch(1), f.FinalizedEpoch()) + require.Equal(t, types.Epoch(2), f.JustifiedCheckpoint().Epoch) + require.Equal(t, types.Epoch(1), f.FinalizedCheckpoint().Epoch) } // Epoch 1 | Epoch 2 @@ -225,10 +226,10 @@ func TestStore_ForkNextEpoch(t *testing.T) { // Insert an attestation to H, H is head f.ProcessAttestation(ctx, []uint64{0}, [32]byte{'h'}, 1) - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(0), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(0), f.JustifiedCheckpoint().Epoch) // D arrives late, D is head state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 0, 0) @@ -236,10 +237,10 @@ func TestStore_ForkNextEpoch(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1)) f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'d'}, headRoot) - require.Equal(t, types.Epoch(1), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(1), f.JustifiedCheckpoint().Epoch) require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight) require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight) } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/vote_test.go b/beacon-chain/forkchoice/doubly-linked-tree/vote_test.go index 80c147b733..b8773aa4d1 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/vote_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/vote_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" @@ -15,7 +16,7 @@ func TestVotes_CanFindHead(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -27,7 +28,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -39,7 +40,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -48,7 +49,7 @@ func TestVotes_CanFindHead(t *testing.T) { // / \ // 2 1 <- +vote, new head f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(1), 2) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(1), r, "Incorrect head for with justified epoch at 1") @@ -57,7 +58,7 @@ func TestVotes_CanFindHead(t *testing.T) { // / \ // vote, new head -> 2 1 f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(2), 2) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -71,7 +72,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -82,7 +83,7 @@ func TestVotes_CanFindHead(t *testing.T) { // | // 3 <- new vote f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(3), 3) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -93,7 +94,7 @@ func TestVotes_CanFindHead(t *testing.T) { // | // 3 <- head f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(1), 3) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head for with justified epoch at 1") @@ -109,7 +110,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -127,7 +128,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -188,7 +189,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 1") @@ -209,9 +210,9 @@ func TestVotes_CanFindHead(t *testing.T) { // 8 // | // 9 <- head - f.store.justifiedEpoch = 2 - f.store.finalizedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(5), balances) + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Root: indexToHash(5), Epoch: 2} + f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Root: indexToHash(5), Epoch: 2} + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -237,7 +238,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -246,28 +247,28 @@ func TestVotes_CanFindHead(t *testing.T) { // The new validators voted for 10. f.ProcessAttestation(context.Background(), []uint64{2, 3, 4}, indexToHash(10), 5) // The new head should be 10. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2") // Set the balances of the last 2 validators to 0. balances = []uint64{1, 1, 1, 0, 0} // The head should be back to 9. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1") // Set the balances back to normal. balances = []uint64{1, 1, 1, 1, 1} // The head should be back to 10. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2") // Remove the last 2 validators. balances = []uint64{1, 1, 1} // The head should be back to 9. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1") @@ -276,7 +277,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, f.store.prune(context.Background(), indexToHash(5))) assert.Equal(t, 11, len(f.store.nodeByRoot), "Incorrect nodes length after prune") - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -300,7 +301,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, f.store.prune(context.Background(), indexToHash(5))) assert.Equal(t, 5, len(f.store.nodeByRoot), "Incorrect nodes length after prune") - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -318,7 +319,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(11), r, "Incorrect head for with justified epoch at 2") } diff --git a/beacon-chain/forkchoice/interfaces.go b/beacon-chain/forkchoice/interfaces.go index cffe22431b..08db3e1ee9 100644 --- a/beacon-chain/forkchoice/interfaces.go +++ b/beacon-chain/forkchoice/interfaces.go @@ -23,7 +23,7 @@ type ForkChoicer interface { // HeadRetriever retrieves head root and optimistic info of the current chain. type HeadRetriever interface { - Head(context.Context, [32]byte, []uint64) ([32]byte, error) + Head(context.Context, []uint64) ([32]byte, error) Tips() ([][32]byte, []types.Slot) IsOptimistic(root [32]byte) (bool, error) } @@ -59,8 +59,8 @@ type Getter interface { AncestorRoot(ctx context.Context, root [32]byte, slot types.Slot) ([]byte, error) CommonAncestorRoot(ctx context.Context, root1 [32]byte, root2 [32]byte) ([32]byte, error) IsCanonical(root [32]byte) bool - FinalizedEpoch() types.Epoch - JustifiedEpoch() types.Epoch + FinalizedCheckpoint() *forkchoicetypes.Checkpoint + JustifiedCheckpoint() *forkchoicetypes.Checkpoint ForkChoiceNodes() []*ethpb.ForkChoiceNode NodeCount() int } @@ -69,6 +69,6 @@ type Getter interface { type Setter interface { SetOptimisticToValid(context.Context, [fieldparams.RootLength]byte) error SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte, [fieldparams.RootLength]byte, [fieldparams.RootLength]byte) ([][32]byte, error) - UpdateJustifiedCheckpoint(*ethpb.Checkpoint) error - UpdateFinalizedCheckpoint(*ethpb.Checkpoint) error + UpdateJustifiedCheckpoint(*forkchoicetypes.Checkpoint) error + UpdateFinalizedCheckpoint(*forkchoicetypes.Checkpoint) error } diff --git a/beacon-chain/forkchoice/protoarray/ffg_update_test.go b/beacon-chain/forkchoice/protoarray/ffg_update_test.go index 4a3b116db9..7d88cbece6 100644 --- a/beacon-chain/forkchoice/protoarray/ffg_update_test.go +++ b/beacon-chain/forkchoice/protoarray/ffg_update_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/beacon-chain/state" v3 "github.com/prysmaticlabs/prysm/beacon-chain/state/v3" "github.com/prysmaticlabs/prysm/config/params" @@ -61,7 +62,7 @@ func TestFFGUpdates_OneBranch(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -91,7 +92,7 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 // | // 3 <- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head for with justified epoch at 0") @@ -103,8 +104,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 <- head // | // 3 - f.store.justifiedEpoch = 1 - r, err = f.Head(context.Background(), indexToHash(2), balances) + jc := &forkchoicetypes.Checkpoint{Epoch: 1, Root: indexToHash(2)} + f.store.justifiedCheckpoint = jc + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head with justified epoch at 1") @@ -116,8 +118,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) { // 2 <- start // | // 3 <- head - f.store.justifiedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(3), balances) + jc = &forkchoicetypes.Checkpoint{Epoch: 2, Root: indexToHash(3)} + f.store.justifiedCheckpoint = jc + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head with justified epoch at 2") } @@ -127,7 +130,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { f := setup(0, 0) ctx := context.Background() - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -188,7 +191,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // 9 10 <-- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head with justified epoch at 0") @@ -218,7 +221,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // head -> 9 10 - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head with justified epoch at 0") @@ -248,19 +251,22 @@ func TestFFGUpdates_TwoBranches(t *testing.T) { // 7 8 // | | // 9 10 <-- head - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head with justified epoch at 0") - f.store.justifiedEpoch = 1 - r, err = f.Head(context.Background(), indexToHash(1), balances) + jc := &forkchoicetypes.Checkpoint{Epoch: 1, Root: indexToHash(1)} + f.store.justifiedCheckpoint = jc + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(7), r, "Incorrect head with justified epoch at 0") } func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { - f := New(justifiedEpoch, finalizedEpoch) + f := New() f.store.nodesIndices[params.BeaconConfig().ZeroHash] = 0 + f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: justifiedEpoch, Root: params.BeaconConfig().ZeroHash} + f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: finalizedEpoch, Root: params.BeaconConfig().ZeroHash} f.store.nodes = append(f.store.nodes, &Node{ slot: 0, root: params.BeaconConfig().ZeroHash, @@ -271,6 +277,5 @@ func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { bestDescendant: NonExistentNode, weight: 0, }) - return f } diff --git a/beacon-chain/forkchoice/protoarray/no_vote_test.go b/beacon-chain/forkchoice/protoarray/no_vote_test.go index 08bc174a3d..94d44a38fd 100644 --- a/beacon-chain/forkchoice/protoarray/no_vote_test.go +++ b/beacon-chain/forkchoice/protoarray/no_vote_test.go @@ -15,7 +15,7 @@ func TestNoVote_CanFindHead(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) if r != params.BeaconConfig().ZeroHash { t.Errorf("Incorrect head with genesis") @@ -28,7 +28,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err := prepareForkchoiceState(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -39,7 +39,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -52,7 +52,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -65,7 +65,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -80,7 +80,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -92,7 +92,8 @@ func TestNoVote_CanFindHead(t *testing.T) { // head -> 4 3 // | // 5 <- starting from 5 with justified epoch 0 should error - _, err = f.Head(context.Background(), indexToHash(5), balances) + f.store.justifiedCheckpoint.Root = indexToHash(5) + _, err = f.Head(context.Background(), balances) wanted := "head at slot 0 with weight 0 is not eligible, finalizedEpoch 1 != 1, justifiedEpoch 2 != 1" require.ErrorContains(t, wanted, err) @@ -104,8 +105,8 @@ func TestNoVote_CanFindHead(t *testing.T) { // 4 3 // | // 5 <- head - f.store.justifiedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(5), balances) + f.store.justifiedCheckpoint.Epoch = 2 + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(5), r, "Incorrect head for with justified epoch at 2") @@ -122,7 +123,7 @@ func TestNoVote_CanFindHead(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2") } diff --git a/beacon-chain/forkchoice/protoarray/proposer_boost_test.go b/beacon-chain/forkchoice/protoarray/proposer_boost_test.go index 09b851225b..7a504c7ac8 100644 --- a/beacon-chain/forkchoice/protoarray/proposer_boost_test.go +++ b/beacon-chain/forkchoice/protoarray/proposer_boost_test.go @@ -35,7 +35,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - headRoot, err := f.Head(ctx, zeroHash, balances) + headRoot, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, headRoot, "Incorrect head with genesis") @@ -57,7 +57,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{0}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 1") @@ -81,7 +81,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{1}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 2") @@ -107,7 +107,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) f.ProcessAttestation(ctx, []uint64{2}, newRoot, fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -142,7 +142,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { SecondsIntoSlot: 0, } require.NoError(t, f.BoostProposerRoot(ctx, args)) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, newRoot, headRoot, "Incorrect head for justified epoch at slot 3") @@ -180,7 +180,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // Regression: process attestations for C, check that it // becomes head, we need two attestations to have C.weight = 30 > 24 = D.weight f.ProcessAttestation(ctx, []uint64{4, 5}, indexToHash(3), fEpoch) - headRoot, err = f.Head(ctx, zeroHash, balances) + headRoot, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), headRoot, "Incorrect head for justified epoch at slot 4") }) @@ -188,7 +188,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -213,7 +213,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { ) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -232,7 +232,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure the head is C, the honest block. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -253,7 +253,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f.ProcessAttestation(ctx, votes, honestBlock, fEpoch) // Ensure the head is STILL C, the honest block, as the honest block had proposer boost. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") }) @@ -261,7 +261,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f := setup(jEpoch, fEpoch) // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -288,7 +288,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -307,7 +307,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is still the head after the malicious proposer reveals their block. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, honestBlock, r, "Incorrect head for justified epoch at slot 2") @@ -326,7 +326,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { f.ProcessAttestation(ctx, votes, maliciouslyWithheldBlock, fEpoch) // Expect the head to have switched to B. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, maliciouslyWithheldBlock, r, "Expected B to become the head") }) @@ -348,7 +348,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { a := zeroHash // The head should always start at the finalized block. - r, err := f.Head(ctx, zeroHash, balances) + r, err := f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, zeroHash, r, "Incorrect head with genesis") @@ -367,7 +367,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Incorrect head for justified epoch at slot 2") @@ -395,7 +395,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // Ensure C is still the head. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Incorrect head for justified epoch at slot 2") @@ -419,7 +419,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) // D cannot win without a boost. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, c, r, "Expected C to remain the head") @@ -435,7 +435,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { require.NoError(t, f.BoostProposerRoot(ctx, args)) // Ensure D becomes the head thanks to boosting. - r, err = f.Head(ctx, zeroHash, balances) + r, err = f.Head(ctx, balances) require.NoError(t, err) assert.Equal(t, d, r, "Expected D to become the head") }) diff --git a/beacon-chain/forkchoice/protoarray/store.go b/beacon-chain/forkchoice/protoarray/store.go index 7c1afce339..304abb0f64 100644 --- a/beacon-chain/forkchoice/protoarray/store.go +++ b/beacon-chain/forkchoice/protoarray/store.go @@ -29,17 +29,17 @@ const defaultPruneThreshold = 256 var lastHeadRoot [32]byte // New initializes a new fork choice store. -func New(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { +func New() *ForkChoice { s := &Store{ - justifiedEpoch: justifiedEpoch, - finalizedEpoch: finalizedEpoch, - proposerBoostRoot: [32]byte{}, - nodes: make([]*Node, 0), - nodesIndices: make(map[[32]byte]uint64), - payloadIndices: make(map[[32]byte]uint64), - canonicalNodes: make(map[[32]byte]bool), - slashedIndices: make(map[types.ValidatorIndex]bool), - pruneThreshold: defaultPruneThreshold, + justifiedCheckpoint: &forkchoicetypes.Checkpoint{}, + finalizedCheckpoint: &forkchoicetypes.Checkpoint{}, + proposerBoostRoot: [32]byte{}, + nodes: make([]*Node, 0), + nodesIndices: make(map[[32]byte]uint64), + payloadIndices: make(map[[32]byte]uint64), + canonicalNodes: make(map[[32]byte]bool), + slashedIndices: make(map[types.ValidatorIndex]bool), + pruneThreshold: defaultPruneThreshold, } b := make([]uint64, 0) @@ -49,11 +49,7 @@ func New(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice { // Head returns the head root from fork choice store. // It firsts computes validator's balance changes then recalculates block tree from leaves to root. -func (f *ForkChoice) Head( - ctx context.Context, - justifiedRoot [32]byte, - justifiedStateBalances []uint64, -) ([32]byte, error) { +func (f *ForkChoice) Head(ctx context.Context, justifiedStateBalances []uint64) ([32]byte, error) { ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.Head") defer span.End() f.votesLock.Lock() @@ -76,7 +72,7 @@ func (f *ForkChoice) Head( } f.balances = newBalances - return f.store.head(ctx, justifiedRoot) + return f.store.head(ctx) } // ProcessAttestation processes attestation for vote accounting, it iterates around validator indices @@ -273,14 +269,18 @@ func (s *Store) PruneThreshold() uint64 { return s.pruneThreshold } -// JustifiedEpoch of fork choice store. -func (f *ForkChoice) JustifiedEpoch() types.Epoch { - return f.store.justifiedEpoch +// JustifiedCheckpoint of fork choice store. +func (f *ForkChoice) JustifiedCheckpoint() *forkchoicetypes.Checkpoint { + f.store.checkpointsLock.RLock() + defer f.store.checkpointsLock.RUnlock() + return f.store.justifiedCheckpoint } -// FinalizedEpoch of fork choice store. -func (f *ForkChoice) FinalizedEpoch() types.Epoch { - return f.store.finalizedEpoch +// FinalizedCheckpoint of fork choice store. +func (f *ForkChoice) FinalizedCheckpoint() *forkchoicetypes.Checkpoint { + f.store.checkpointsLock.RLock() + defer f.store.checkpointsLock.RUnlock() + return f.store.finalizedCheckpoint } // proposerBoost of fork choice store. @@ -291,20 +291,23 @@ func (s *Store) proposerBoost() [fieldparams.RootLength]byte { } // head starts from justified root and then follows the best descendant links -// to find the best block for head. -func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, error) { +// to find the best block for head. It assumes the caller has a lock on nodes. +func (s *Store) head(ctx context.Context) ([32]byte, error) { ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.head") defer span.End() // Justified index has to be valid in node indices map, and can not be out of bound. - justifiedIndex, ok := s.nodesIndices[justifiedRoot] + if s.justifiedCheckpoint == nil { + return [32]byte{}, errInvalidNilCheckpoint + } + + justifiedIndex, ok := s.nodesIndices[s.justifiedCheckpoint.Root] if !ok { return [32]byte{}, errUnknownJustifiedRoot } if justifiedIndex >= uint64(len(s.nodes)) { return [32]byte{}, errInvalidJustifiedIndex } - justifiedNode := s.nodes[justifiedIndex] bestDescendantIndex := justifiedNode.bestDescendant // If the justified node doesn't have a best descendant, @@ -315,12 +318,11 @@ func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, err if bestDescendantIndex >= uint64(len(s.nodes)) { return [32]byte{}, errInvalidBestDescendantIndex } - bestNode := s.nodes[bestDescendantIndex] if !s.viableForHead(bestNode) { return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch %d != %d, justifiedEpoch %d != %d", - bestNode.slot, bestNode.weight/10e9, bestNode.finalizedEpoch, s.finalizedEpoch, bestNode.justifiedEpoch, s.justifiedEpoch) + bestNode.slot, bestNode.weight/10e9, bestNode.finalizedEpoch, s.finalizedCheckpoint.Epoch, bestNode.justifiedEpoch, s.justifiedCheckpoint.Epoch) } // Update metrics. @@ -743,10 +745,12 @@ func (s *Store) leadsToViableHead(node *Node) (bool, error) { // Any node with diff finalized or justified epoch than the ones in fork choice store // should not be viable to head. func (s *Store) viableForHead(node *Node) bool { + s.checkpointsLock.RLock() + defer s.checkpointsLock.RUnlock() // `node` is viable if its justified epoch and finalized epoch are the same as the one in `Store`. // It's also viable if we are in genesis epoch. - justified := s.justifiedEpoch == node.justifiedEpoch || s.justifiedEpoch == 0 - finalized := s.finalizedEpoch == node.finalizedEpoch || s.finalizedEpoch == 0 + justified := s.justifiedCheckpoint.Epoch == node.justifiedEpoch || s.justifiedCheckpoint.Epoch == 0 + finalized := s.finalizedCheckpoint.Epoch == node.finalizedEpoch || s.finalizedCheckpoint.Epoch == 0 return justified && finalized } @@ -857,25 +861,25 @@ func (f *ForkChoice) InsertSlashedIndex(ctx context.Context, index types.Validat } } -// UpdateJustifiedCheckpoint sets the justified epoch to the given one -func (f *ForkChoice) UpdateJustifiedCheckpoint(jc *pbrpc.Checkpoint) error { +// UpdateJustifiedCheckpoint sets the justified checkpoint to the given one +func (f *ForkChoice) UpdateJustifiedCheckpoint(jc *forkchoicetypes.Checkpoint) error { if jc == nil { return errInvalidNilCheckpoint } - f.store.nodesLock.Lock() - defer f.store.nodesLock.Unlock() - f.store.justifiedEpoch = jc.Epoch + f.store.checkpointsLock.Lock() + defer f.store.checkpointsLock.Unlock() + f.store.justifiedCheckpoint = jc return nil } -// UpdateFinalizedCheckpoint sets the finalized epoch to the given one -func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *pbrpc.Checkpoint) error { +// UpdateFinalizedCheckpoint sets the finalized checkpoint to the given one +func (f *ForkChoice) UpdateFinalizedCheckpoint(fc *forkchoicetypes.Checkpoint) error { if fc == nil { return errInvalidNilCheckpoint } - f.store.nodesLock.Lock() - defer f.store.nodesLock.Unlock() - f.store.finalizedEpoch = fc.Epoch + f.store.checkpointsLock.Lock() + defer f.store.checkpointsLock.Unlock() + f.store.finalizedCheckpoint = fc return nil } @@ -895,7 +899,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho } if err := f.store.insert(ctx, b.Slot(), r, parentRoot, payloadHash, - chain[i].JustifiedEpoch, chain[i].FinalizedEpoch); err != nil { + chain[i].JustifiedCheckpoint.Epoch, chain[i].FinalizedCheckpoint.Epoch); err != nil { return err } } diff --git a/beacon-chain/forkchoice/protoarray/store_test.go b/beacon-chain/forkchoice/protoarray/store_test.go index eb9d2183df..4282d7888d 100644 --- a/beacon-chain/forkchoice/protoarray/store_test.go +++ b/beacon-chain/forkchoice/protoarray/store_test.go @@ -28,13 +28,13 @@ func TestStore_PruneThreshold(t *testing.T) { func TestStore_JustifiedEpoch(t *testing.T) { j := types.Epoch(100) f := setup(j, j) - require.Equal(t, j, f.JustifiedEpoch()) + require.Equal(t, j, f.JustifiedCheckpoint().Epoch) } func TestStore_FinalizedEpoch(t *testing.T) { j := types.Epoch(50) f := setup(j, j) - require.Equal(t, j, f.FinalizedEpoch()) + require.Equal(t, j, f.FinalizedCheckpoint().Epoch) } func TestForkChoice_HasNode(t *testing.T) { @@ -51,8 +51,9 @@ func TestForkChoice_HasNode(t *testing.T) { func TestStore_Head_UnknownJustifiedRoot(t *testing.T) { s := &Store{nodesIndices: make(map[[32]byte]uint64)} + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: [32]byte{'a'}} - _, err := s.head(context.Background(), [32]byte{}) + _, err := s.head(context.Background()) assert.ErrorContains(t, errUnknownJustifiedRoot.Error(), err) } @@ -61,8 +62,9 @@ func TestStore_Head_UnknownJustifiedIndex(t *testing.T) { indices := make(map[[32]byte]uint64) indices[r] = 1 s := &Store{nodesIndices: indices} + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} - _, err := s.head(context.Background(), r) + _, err := s.head(context.Background()) assert.ErrorContains(t, errInvalidJustifiedIndex.Error(), err) } @@ -73,7 +75,9 @@ func TestStore_Head_Itself(t *testing.T) { // Since the justified node does not have a best descendant so the best node // is itself. s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, parent: NonExistentNode, bestDescendant: NonExistentNode}}, canonicalNodes: make(map[[32]byte]bool)} - h, err := s.head(context.Background(), r) + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + h, err := s.head(context.Background()) require.NoError(t, err) assert.Equal(t, r, h) } @@ -86,7 +90,9 @@ func TestStore_Head_BestDescendant(t *testing.T) { // Since the justified node's best descendant is at index 1, and its root is `best`, // the head should be `best`. s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, bestDescendant: 1, parent: NonExistentNode}, {root: best, parent: 0}}, canonicalNodes: make(map[[32]byte]bool)} - h, err := s.head(context.Background(), r) + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + h, err := s.head(context.Background()) require.NoError(t, err) assert.Equal(t, best, h) } @@ -99,7 +105,9 @@ func TestStore_Head_ContextCancelled(t *testing.T) { s := &Store{nodesIndices: indices, nodes: []*Node{{root: r, parent: NonExistentNode, bestDescendant: 1}, {root: best, parent: 0}}, canonicalNodes: make(map[[32]byte]bool)} cancel() - _, err := s.head(ctx, r) + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 0, Root: r} + _, err := s.head(ctx) require.ErrorContains(t, "context canceled", err) } @@ -123,6 +131,8 @@ func TestStore_Insert_KnownParent(t *testing.T) { p := [32]byte{'B'} s.nodesIndices[p] = 0 payloadHash := [32]byte{'c'} + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{} require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, payloadHash, 1, 1)) assert.Equal(t, 2, len(s.nodes), "Did not insert block") assert.Equal(t, 2, len(s.nodesIndices), "Did not insert block") @@ -150,6 +160,8 @@ func TestStore_ApplyScoreChanges_UpdateWeightsPositiveDelta(t *testing.T) { // Each node gets one unique vote. The weight should look like 103 <- 102 <- 101 because // they get propagated back. + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{} require.NoError(t, s.applyWeightChanges(context.Background(), []uint64{}, []int{1, 1, 1})) assert.Equal(t, uint64(103), s.nodes[0].weight) assert.Equal(t, uint64(102), s.nodes[1].weight) @@ -165,6 +177,8 @@ func TestStore_ApplyScoreChanges_UpdateWeightsNegativeDelta(t *testing.T) { // Each node gets one unique vote which contributes to negative delta. // The weight should look like 97 <- 98 <- 99 because they get propagated back. + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{} require.NoError(t, s.applyWeightChanges(context.Background(), []uint64{}, []int{-1, -1, -1})) assert.Equal(t, uint64(97), s.nodes[0].weight) assert.Equal(t, uint64(98), s.nodes[1].weight) @@ -179,6 +193,8 @@ func TestStore_ApplyScoreChanges_UpdateWeightsMixedDelta(t *testing.T) { {parent: 1, root: [32]byte{'A'}, weight: 100}}} // Each node gets one mixed vote. The weight should look like 100 <- 200 <- 250. + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{} require.NoError(t, s.applyWeightChanges(context.Background(), []uint64{}, []int{-100, -50, 150})) assert.Equal(t, uint64(100), s.nodes[0].weight) assert.Equal(t, uint64(200), s.nodes[1].weight) @@ -187,7 +203,9 @@ func TestStore_ApplyScoreChanges_UpdateWeightsMixedDelta(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_RemoveChild(t *testing.T) { // Make parent's best child equal's to input child index and child is not viable. - s := &Store{nodes: []*Node{{bestChild: 1}, {}}, justifiedEpoch: 1, finalizedEpoch: 1} + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} + s := &Store{nodes: []*Node{{bestChild: 1}, {}}, justifiedCheckpoint: jc, finalizedCheckpoint: fc} require.NoError(t, s.updateBestChildAndDescendant(0, 1)) // Verify parent's best child and best descendant are `none`. @@ -198,6 +216,8 @@ func TestStore_UpdateBestChildAndDescendant_RemoveChild(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_UpdateDescendant(t *testing.T) { // Make parent's best child equal to child index and child is viable. s := &Store{nodes: []*Node{{bestChild: 1}, {bestDescendant: NonExistentNode}}} + s.justifiedCheckpoint = &forkchoicetypes.Checkpoint{} + s.finalizedCheckpoint = &forkchoicetypes.Checkpoint{} require.NoError(t, s.updateBestChildAndDescendant(0, 1)) // Verify parent's best child is the same and best descendant is not set to child index. @@ -208,9 +228,11 @@ func TestStore_UpdateBestChildAndDescendant_UpdateDescendant(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_ChangeChildByViability(t *testing.T) { // Make parent's best child not equal to child index, child leads to viable index and // parent's best child doesn't lead to viable index. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} @@ -224,9 +246,11 @@ func TestStore_UpdateBestChildAndDescendant_ChangeChildByViability(t *testing.T) func TestStore_UpdateBestChildAndDescendant_ChangeChildByWeight(t *testing.T) { // Make parent's best child not equal to child index, child leads to viable index and // parents best child leads to viable index but child has more weight than parent's best child. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1}}} @@ -239,9 +263,11 @@ func TestStore_UpdateBestChildAndDescendant_ChangeChildByWeight(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_ChangeChildAtLeaf(t *testing.T) { // Make parent's best child to none and input child leads to viable index. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} @@ -255,9 +281,11 @@ func TestStore_UpdateBestChildAndDescendant_ChangeChildAtLeaf(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_NoChangeByViability(t *testing.T) { // Make parent's best child not equal to child index, child leads to not viable index and // parents best child leads to viable index. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode}}} @@ -271,9 +299,11 @@ func TestStore_UpdateBestChildAndDescendant_NoChangeByViability(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_NoChangeByWeight(t *testing.T) { // Make parent's best child not equal to child index, child leads to viable index and // parents best child leads to viable index but parent's best child has more weight. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: 1, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1, weight: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}}} @@ -286,9 +316,11 @@ func TestStore_UpdateBestChildAndDescendant_NoChangeByWeight(t *testing.T) { func TestStore_UpdateBestChildAndDescendant_NoChangeAtLeaf(t *testing.T) { // Make parent's best child to none and input child does not lead to viable index. + jc := &forkchoicetypes.Checkpoint{Epoch: 1} + fc := &forkchoicetypes.Checkpoint{Epoch: 1} s := &Store{ - justifiedEpoch: 1, - finalizedEpoch: 1, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, nodes: []*Node{{bestChild: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode, justifiedEpoch: 1, finalizedEpoch: 1}, {bestDescendant: NonExistentNode}}} @@ -786,10 +818,12 @@ func TestStore_LeadsToViableHead(t *testing.T) { {&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true}, } for _, tc := range tests { + jc := &forkchoicetypes.Checkpoint{Epoch: tc.justifiedEpoch} + fc := &forkchoicetypes.Checkpoint{Epoch: tc.finalizedEpoch} s := &Store{ - justifiedEpoch: tc.justifiedEpoch, - finalizedEpoch: tc.finalizedEpoch, - nodes: []*Node{tc.n}, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, + nodes: []*Node{tc.n}, } got, err := s.leadsToViableHead(tc.n) require.NoError(t, err) @@ -812,9 +846,11 @@ func TestStore_ViableForHead(t *testing.T) { {&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true}, } for _, tc := range tests { + jc := &forkchoicetypes.Checkpoint{Epoch: tc.justifiedEpoch} + fc := &forkchoicetypes.Checkpoint{Epoch: tc.finalizedEpoch} s := &Store{ - justifiedEpoch: tc.justifiedEpoch, - finalizedEpoch: tc.finalizedEpoch, + justifiedCheckpoint: jc, + finalizedCheckpoint: fc, } assert.Equal(t, tc.want, s.viableForHead(tc.n)) } @@ -984,7 +1020,7 @@ func TestStore_RemoveEquivocating(t *testing.T) { state, blkRoot, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - head, err := f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{}) + head, err := f.Head(ctx, []uint64{}) require.NoError(t, err) require.Equal(t, [32]byte{'a'}, head) @@ -995,20 +1031,20 @@ func TestStore_RemoveEquivocating(t *testing.T) { state, blkRoot, err = prepareForkchoiceState(ctx, 3, [32]byte{'c'}, [32]byte{'a'}, [32]byte{'C'}, 1, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{}) + head, err = f.Head(ctx, []uint64{}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) // Insert two attestations for block b, it becomes head f.ProcessAttestation(ctx, []uint64{1, 2}, [32]byte{'b'}, 1) f.ProcessAttestation(ctx, []uint64{3}, [32]byte{'c'}, 1) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.NoError(t, err) require.Equal(t, [32]byte{'b'}, head) // Process b's slashing, c is now head f.InsertSlashedIndex(ctx, 1) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) require.Equal(t, uint64(200), f.store.nodes[2].weight) @@ -1016,7 +1052,7 @@ func TestStore_RemoveEquivocating(t *testing.T) { // Process the same slashing again, should be a noop f.InsertSlashedIndex(ctx, 1) - head, err = f.Head(ctx, params.BeaconConfig().ZeroHash, []uint64{100, 200, 200, 300}) + head, err = f.Head(ctx, []uint64{100, 200, 200, 300}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) require.Equal(t, uint64(200), f.store.nodes[2].weight) @@ -1032,12 +1068,12 @@ func TestStore_UpdateCheckpoints(t *testing.T) { f := setup(1, 1) jr := [32]byte{'j'} fr := [32]byte{'f'} - jc := ðpb.Checkpoint{Root: jr[:], Epoch: 3} - fc := ðpb.Checkpoint{Root: fr[:], Epoch: 2} + jc := &forkchoicetypes.Checkpoint{Root: jr, Epoch: 3} + fc := &forkchoicetypes.Checkpoint{Root: fr, Epoch: 2} require.NoError(t, f.UpdateJustifiedCheckpoint(jc)) require.NoError(t, f.UpdateFinalizedCheckpoint(fc)) - require.Equal(t, f.store.justifiedEpoch, jc.Epoch) - require.Equal(t, f.store.finalizedEpoch, fc.Epoch) + require.Equal(t, f.store.justifiedCheckpoint, jc) + require.Equal(t, f.store.finalizedCheckpoint, fc) } func TestStore_InsertOptimisticChain(t *testing.T) { @@ -1051,8 +1087,10 @@ func TestStore_InsertOptimisticChain(t *testing.T) { require.NoError(t, err) wsb, err := wrapper.WrappedSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), JustifiedEpoch: 1, - FinalizedEpoch: 1}) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + }) for i := uint64(2); i < 11; i++ { blk := util.NewBeaconBlock() blk.Block.Slot = types.Slot(i) @@ -1060,8 +1098,10 @@ func TestStore_InsertOptimisticChain(t *testing.T) { blk.Block.ParentRoot = copiedRoot[:] wsb, err = wrapper.WrappedSignedBeaconBlock(blk) require.NoError(t, err) - blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), JustifiedEpoch: 1, - FinalizedEpoch: 1}) + blks = append(blks, &forkchoicetypes.BlockAndCheckpoints{Block: wsb.Block(), + JustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]}, + }) root, err = blk.Block.HashTreeRoot() require.NoError(t, err) } diff --git a/beacon-chain/forkchoice/protoarray/types.go b/beacon-chain/forkchoice/protoarray/types.go index bb9b567196..efc08031a3 100644 --- a/beacon-chain/forkchoice/protoarray/types.go +++ b/beacon-chain/forkchoice/protoarray/types.go @@ -3,6 +3,7 @@ package protoarray import ( "sync" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" ) @@ -18,8 +19,8 @@ type ForkChoice struct { // Store defines the fork choice store which includes block nodes and the last view of checkpoint information. type Store struct { pruneThreshold uint64 // do not prune tree unless threshold is reached. - justifiedEpoch types.Epoch // latest justified epoch in store. - finalizedEpoch types.Epoch // latest finalized epoch in store. + justifiedCheckpoint *forkchoicetypes.Checkpoint // latest justified checkpoint in store. + finalizedCheckpoint *forkchoicetypes.Checkpoint // latest finalized checkpoint in store. proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner. previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner. previousProposerBoostScore uint64 // previous proposer boosted root score. @@ -30,6 +31,7 @@ type Store struct { slashedIndices map[types.ValidatorIndex]bool // The list of equivocating validators nodesLock sync.RWMutex proposerBoostLock sync.RWMutex + checkpointsLock sync.RWMutex } // Node defines the individual block which includes its block parent, ancestor and how much weight accounted for it. diff --git a/beacon-chain/forkchoice/protoarray/unrealized_justification.go b/beacon-chain/forkchoice/protoarray/unrealized_justification.go index 2567681a5c..eacff9a6e5 100644 --- a/beacon-chain/forkchoice/protoarray/unrealized_justification.go +++ b/beacon-chain/forkchoice/protoarray/unrealized_justification.go @@ -55,11 +55,11 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() { for _, node := range f.store.nodes { node.justifiedEpoch = node.unrealizedJustifiedEpoch node.finalizedEpoch = node.unrealizedFinalizedEpoch - if node.justifiedEpoch > f.store.justifiedEpoch { - f.store.justifiedEpoch = node.justifiedEpoch + if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch { + f.store.justifiedCheckpoint.Epoch = node.justifiedEpoch } - if node.finalizedEpoch > f.store.finalizedEpoch { - f.store.finalizedEpoch = node.finalizedEpoch + if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch { + f.store.finalizedCheckpoint.Epoch = node.finalizedEpoch } } } diff --git a/beacon-chain/forkchoice/protoarray/unrealized_justification_test.go b/beacon-chain/forkchoice/protoarray/unrealized_justification_test.go index 2760717286..53e498bdba 100644 --- a/beacon-chain/forkchoice/protoarray/unrealized_justification_test.go +++ b/beacon-chain/forkchoice/protoarray/unrealized_justification_test.go @@ -5,9 +5,9 @@ import ( "context" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/require" ) @@ -81,25 +81,25 @@ func TestStore_LongFork(t *testing.T) { // Add an attestation to c, it is head f.ProcessAttestation(ctx, []uint64{0}, [32]byte{'c'}, 1) - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, headRoot) // D is head even though its weight is lower. - hr := [32]byte{'d'} - state, blkRoot, err = prepareForkchoiceState(ctx, 103, hr, [32]byte{'b'}, [32]byte{'D'}, 2, 1) + ha := [32]byte{'a'} + state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'b'}, [32]byte{'D'}, 2, 1) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - require.NoError(t, f.UpdateJustifiedCheckpoint(ðpb.Checkpoint{Epoch: 2, Root: hr[:]})) - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + require.NoError(t, f.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 2, Root: ha})) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) - require.Equal(t, hr, headRoot) + require.Equal(t, [32]byte{'d'}, headRoot) require.Equal(t, uint64(0), f.store.nodes[4].weight) require.Equal(t, uint64(100), f.store.nodes[3].weight) // Update unrealized justification, c becomes head f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, headRoot) } @@ -158,30 +158,31 @@ func TestStore_NoDeadLock(t *testing.T) { // Epoch 3 // Current Head is H - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(0), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(0), f.JustifiedCheckpoint().Epoch) // Insert Block I, it becomes Head hr := [32]byte{'i'} state, blkRoot, err = prepareForkchoiceState(ctx, 108, hr, [32]byte{'f'}, [32]byte{'I'}, 1, 0) require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - require.NoError(t, f.UpdateJustifiedCheckpoint(ðpb.Checkpoint{Epoch: 1, Root: hr[:]})) - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + ha := [32]byte{'a'} + require.NoError(t, f.UpdateJustifiedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: 1, Root: ha})) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, hr, headRoot) - require.Equal(t, types.Epoch(1), f.JustifiedEpoch()) - require.Equal(t, types.Epoch(0), f.FinalizedEpoch()) + require.Equal(t, types.Epoch(1), f.JustifiedCheckpoint().Epoch) + require.Equal(t, types.Epoch(0), f.FinalizedCheckpoint().Epoch) // Realized Justified checkpoints, H becomes head f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(2), f.JustifiedEpoch()) - require.Equal(t, types.Epoch(1), f.FinalizedEpoch()) + require.Equal(t, types.Epoch(2), f.JustifiedCheckpoint().Epoch) + require.Equal(t, types.Epoch(1), f.FinalizedCheckpoint().Epoch) } // Epoch 1 | Epoch 2 @@ -226,10 +227,10 @@ func TestStore_ForkNextEpoch(t *testing.T) { // Insert an attestation to H, H is head f.ProcessAttestation(ctx, []uint64{0}, [32]byte{'h'}, 1) - headRoot, err := f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err := f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) - require.Equal(t, types.Epoch(0), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(0), f.JustifiedCheckpoint().Epoch) // D arrives late, D is head state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 0, 0) @@ -237,10 +238,10 @@ func TestStore_ForkNextEpoch(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1)) f.UpdateUnrealizedCheckpoints() - headRoot, err = f.Head(ctx, [32]byte{}, []uint64{100}) + headRoot, err = f.Head(ctx, []uint64{100}) require.NoError(t, err) require.Equal(t, [32]byte{'d'}, headRoot) - require.Equal(t, types.Epoch(1), f.JustifiedEpoch()) + require.Equal(t, types.Epoch(1), f.JustifiedCheckpoint().Epoch) // nodes[8] = D since it's late! require.Equal(t, uint64(0), f.store.nodes[8].weight) require.Equal(t, uint64(100), f.store.nodes[7].weight) diff --git a/beacon-chain/forkchoice/protoarray/vote_test.go b/beacon-chain/forkchoice/protoarray/vote_test.go index 9fe2866dda..9852443898 100644 --- a/beacon-chain/forkchoice/protoarray/vote_test.go +++ b/beacon-chain/forkchoice/protoarray/vote_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" @@ -15,7 +16,7 @@ func TestVotes_CanFindHead(t *testing.T) { ctx := context.Background() // The head should always start at the finalized block. - r, err := f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err := f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, params.BeaconConfig().ZeroHash, r, "Incorrect head with genesis") @@ -27,7 +28,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -39,7 +40,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -48,7 +49,7 @@ func TestVotes_CanFindHead(t *testing.T) { // / \ // 2 1 <- +vote, new head f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(1), 2) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(1), r, "Incorrect head for with justified epoch at 1") @@ -57,7 +58,7 @@ func TestVotes_CanFindHead(t *testing.T) { // / \ // vote, new head -> 2 1 f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(2), 2) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -71,7 +72,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -82,7 +83,7 @@ func TestVotes_CanFindHead(t *testing.T) { // | // 3 <- new vote f.ProcessAttestation(context.Background(), []uint64{0}, indexToHash(3), 3) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1") @@ -93,7 +94,7 @@ func TestVotes_CanFindHead(t *testing.T) { // | // 3 <- head f.ProcessAttestation(context.Background(), []uint64{1}, indexToHash(1), 3) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(3), r, "Incorrect head for with justified epoch at 1") @@ -109,7 +110,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -127,7 +128,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1") @@ -188,7 +189,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), params.BeaconConfig().ZeroHash, balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 1") @@ -209,9 +210,11 @@ func TestVotes_CanFindHead(t *testing.T) { // 8 // | // 9 <- head - f.store.justifiedEpoch = 2 - f.store.finalizedEpoch = 2 - r, err = f.Head(context.Background(), indexToHash(5), balances) + jc := &forkchoicetypes.Checkpoint{Epoch: 2, Root: indexToHash(5)} + fc := &forkchoicetypes.Checkpoint{Epoch: 2, Root: indexToHash(5)} + f.store.justifiedCheckpoint = jc + f.store.finalizedCheckpoint = fc + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -237,7 +240,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -246,28 +249,28 @@ func TestVotes_CanFindHead(t *testing.T) { // The new validators voted for 10. f.ProcessAttestation(context.Background(), []uint64{2, 3, 4}, indexToHash(10), 5) // The new head should be 10. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2") // Set the balances of the last 2 validators to 0. balances = []uint64{1, 1, 1, 0, 0} // The head should be back to 9. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1") // Set the balances back to normal. balances = []uint64{1, 1, 1, 1, 1} // The head should be back to 10. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(10), r, "Incorrect head for with justified epoch at 2") // Remove the last 2 validators. balances = []uint64{1, 1, 1} // The head should be back to 9. - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 1") @@ -276,7 +279,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, f.store.prune(context.Background(), indexToHash(5))) assert.Equal(t, 11, len(f.store.nodes), "Incorrect nodes length after prune") - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -300,7 +303,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, f.store.prune(context.Background(), indexToHash(5))) assert.Equal(t, 5, len(f.store.nodes), "Incorrect nodes length after prune") - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(9), r, "Incorrect head for with justified epoch at 2") @@ -318,7 +321,7 @@ func TestVotes_CanFindHead(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, state, blkRoot)) - r, err = f.Head(context.Background(), indexToHash(5), balances) + r, err = f.Head(context.Background(), balances) require.NoError(t, err) assert.Equal(t, indexToHash(11), r, "Incorrect head for with justified epoch at 2") } diff --git a/beacon-chain/forkchoice/types/BUILD.bazel b/beacon-chain/forkchoice/types/BUILD.bazel index 65cd8963a0..dc341b6805 100644 --- a/beacon-chain/forkchoice/types/BUILD.bazel +++ b/beacon-chain/forkchoice/types/BUILD.bazel @@ -6,7 +6,9 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types", visibility = ["//visibility:public"], deps = [ + "//config/fieldparams:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", ], ) diff --git a/beacon-chain/forkchoice/types/types.go b/beacon-chain/forkchoice/types/types.go index 5b9e79555e..e3d2775b92 100644 --- a/beacon-chain/forkchoice/types/types.go +++ b/beacon-chain/forkchoice/types/types.go @@ -1,8 +1,10 @@ package types import ( + fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/consensus-types/interfaces" types "github.com/prysmaticlabs/prysm/consensus-types/primitives" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) // ProposerBoostRootArgs to call the BoostProposerRoot function. @@ -13,9 +15,17 @@ type ProposerBoostRootArgs struct { SecondsIntoSlot uint64 } +// Checkpoint is an array version of ethpb.Checkpoint. It is used internally in +// forkchoice, while the slice version is used in the interface to legagy code +// in other packages +type Checkpoint struct { + Epoch types.Epoch + Root [fieldparams.RootLength]byte +} + // BlockAndCheckpoints to call the InsertOptimisticChain function type BlockAndCheckpoints struct { - Block interfaces.BeaconBlock - JustifiedEpoch types.Epoch - FinalizedEpoch types.Epoch + Block interfaces.BeaconBlock + JustifiedCheckpoint *ethpb.Checkpoint + FinalizedCheckpoint *ethpb.Checkpoint } diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index dde89c94f0..577624e0e3 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -348,9 +348,9 @@ func (b *BeaconNode) Close() { func (b *BeaconNode) startForkChoice() { if features.Get().EnableForkChoiceDoublyLinkedTree { - b.forkChoiceStore = doublylinkedtree.New(0, 0) + b.forkChoiceStore = doublylinkedtree.New() } else { - b.forkChoiceStore = protoarray.New(0, 0) + b.forkChoiceStore = protoarray.New() } } diff --git a/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice.go b/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice.go index 5367f2a006..cc51fd9f95 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice.go +++ b/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice.go @@ -12,8 +12,8 @@ func (ds *Server) GetForkChoice(_ context.Context, _ *empty.Empty) (*pbrpc.ForkC store := ds.ForkFetcher.ForkChoicer() return &pbrpc.ForkChoiceResponse{ - JustifiedEpoch: store.JustifiedEpoch(), - FinalizedEpoch: store.FinalizedEpoch(), + JustifiedEpoch: store.JustifiedCheckpoint().Epoch, + FinalizedEpoch: store.FinalizedCheckpoint().Epoch, ForkchoiceNodes: store.ForkChoiceNodes(), }, nil } diff --git a/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice_test.go b/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice_test.go index 7d236a5395..85bf0200c2 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/debug/forkchoice_test.go @@ -12,10 +12,10 @@ import ( ) func TestServer_GetForkChoice_ProtoArray(t *testing.T) { - store := protoarray.New(0, 0) + store := protoarray.New() bs := &Server{ForkFetcher: &mock.ChainService{ForkChoiceStore: store}} res, err := bs.GetForkChoice(context.Background(), &empty.Empty{}) require.NoError(t, err) - assert.Equal(t, store.JustifiedEpoch(), res.JustifiedEpoch, "Did not get wanted justified epoch") - assert.Equal(t, store.FinalizedEpoch(), res.FinalizedEpoch, "Did not get wanted finalized epoch") + assert.Equal(t, store.JustifiedCheckpoint().Epoch, res.JustifiedEpoch, "Did not get wanted justified epoch") + assert.Equal(t, store.FinalizedCheckpoint().Epoch, res.FinalizedEpoch, "Did not get wanted finalized epoch") } diff --git a/testing/spectest/shared/common/forkchoice/service.go b/testing/spectest/shared/common/forkchoice/service.go index 01077d0f2e..fc4480bbf0 100644 --- a/testing/spectest/shared/common/forkchoice/service.go +++ b/testing/spectest/shared/common/forkchoice/service.go @@ -52,7 +52,7 @@ func startChainService(t testing.TB, st state.BeaconState, block interfaces.Sign blockchain.WithFinalizedStateAtStartUp(st), blockchain.WithDatabase(db), blockchain.WithAttestationService(attPool), - blockchain.WithForkChoiceStore(protoarray.New(0, 0)), + blockchain.WithForkChoiceStore(protoarray.New()), blockchain.WithStateGen(stategen.New(db)), blockchain.WithStateNotifier(&mock.MockStateNotifier{}), blockchain.WithAttestationPool(attestations.NewPool()),