Compare commits

...

23 Commits

Author SHA1 Message Date
Raul Jordan
a89a51af06 fix up via interface 2022-06-10 10:42:03 -04:00
Nishant Das
7f443e8387 Add Optimistic Sync Scenario Testing (#10836)
* add latest changes

* fix it

* add multiclient support

* fix tests

* Apply suggestions from code review

* fix test

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-09 23:24:53 +00:00
Potuz
18fc17c903 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>
2022-06-09 22:28:30 +00:00
Sammy Rosso
d7b01b9d81 Fix default mainnet log when using chain config (#10855)
* Fix default mainnet log when using chain config

Add a log to specify the use of a chain-config-file rather than
defaulting to that of mainnet.
Related to #10821.

* Add more specific log message

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-09 21:31:59 +00:00
terencechain
7ebd9035dd Add ttd metric (#10851)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-09 20:35:38 +00:00
Radosław Kapka
578fea73d7 API's IsOptimistic - update header.StateRoot only when block is not missing (#10852)
* bug fix

* tests

* comment

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-09 19:35:35 +00:00
terencechain
7fcadbe3ef Add optimistic status to chainhead (#10842)
* Add optimistic status to chainhead

* Fix tests

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-09 17:37:52 +00:00
Radosław Kapka
fce9e6883d Native Blocks Ep. 1 - New types and functions (#10837)
* types and functions

* partially done tests

* refactor

* remaining Proto() tests

* remaining proto.go tests

* simplify UnmarshalSSZ and move BeaconBlockIsNil

* getters_test

* remove errAssertionFailed

* review feedback

* remove cloning protobuf

* fmt

* change IsNil

* fix tests
2022-06-09 13:13:02 +00:00
terencechain
5ee66a4a68 Update engine api buckets (#10847)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-08 20:12:54 +00:00
kasey
f4c7fb6182 checkpoint sync e2e to use 3m timeout w/ elapsed log (#10849)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-06-08 19:14:10 +00:00
Potuz
077dcdc643 enable dev on Ropsten (#10839)
* enable dev on Ropsten

* include only vectorized HTR and doublylinkedtree

* error handling

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-08 13:45:13 +00:00
kasey
1fa864cb1a use slot:block index correctly (#10820)
* adding splitRoots, refactor to use it

* use splitRoots & work in roots only

the most common use case for this method is to get a list of
candidate roots and check if they are canonical. there isn't a great
reason to look up all the non-canonical blocks, because forkchoice
checks based on the root only, so just return roots and defer the
responsibility of resolving those to full blocks.

* update comment

* clean up shadowing

* more clear non-error return

* add test case for single root in index slot

* fmt

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-07 16:47:42 +00:00
Mike Neuder
6357860cc2 Refactor validator accounts backup to remove cli context dependency (#10824)
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2022-06-07 15:19:12 +00:00
mick
cc1ea81d4a Implement generate-auth-secret on beacon node CLI (#10733)
* s

* s

* typo

* typo

* s

* s

* fixes based on PR feedback

* PR feedback

* reverting log changes

* adding flag per feedback

* conventions

* main fixes

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* s

* tests

* test attempt

* test

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* err fix

* s

* further simplify

* cleanup

* namefix

* tests pass

* gaz

* rem deadcode

* Gaz

* shorthand

* naming

* test pass

* dedup

* success

* Ignore jwt.hex file

* logrus

* feedback

* junk

* Also check that no file was written

* local run config

* small fix

* jwt

* testfix

* s

* disabling test

* reverting main changes

* main revert

* removing temp folder

* comment

* gaz

* clarity

* rem

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-06-07 07:37:12 +00:00
Nishant Das
dd65622441 Efficiently Pack Uint64 Lists (#10830)
* make it more efficient

* radek's review

* review again
2022-06-07 06:34:13 +00:00
Nishant Das
6c39301f33 Integrate Engine Proxy into E2E (#10808)
* add it in

* support jwt secret

* fix it

* fix

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-06 23:35:54 +00:00
terencechain
5b12f5a27d Integrate builder client into builder service (#10825)
* Integrate builder client into builder service

* Do nothing if pubkey is not found

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-06 22:38:49 +00:00
terencechain
105d73d59b Update spec labels (#10833)
* Update label version

* Update label version

* Update label version

* Update label version

* Update label version
2022-06-06 18:13:59 -04:00
james-prysm
db687bf56d Validator client builder support (#10749)
* Startinb builder service and interface

* Get header from builder

* Add get builder block

* Single validator registration

* Add mev-builder http cli flag

* Add method to verify registration signature

* Add builder registration

* Add submit validator registration

* suporting yaml

* fix yaml unmarshaling

* rolling back some changes from unmarshal from file

* adding yaml support

* adding register validator support

* added new validator requests into client/validator

* fixing gofmt

* updating flags and including gas limit, unit tests are still broken

* fixing bazel

* more name changes and fixing unit tests

* fixing unit tests and renaming functions

* fixing unit tests and renaming to match changes

* adding new test for yaml

* fixing bazel linter

* reverting change on validator service proto

* adding clarifying logs

* renaming function name to be more descriptive

* renaming variable

* rolling back some files that will be added from the builder-1 branch

* reverting more

* more reverting

* need placeholder

* need placeholder

* fixing unit test

* fixing unit test

* fixing unit test

* fixing unit test

* fixing more unit tests

* fixing more unit tests

* rolling back mockgen

* fixing bazel

* rolling back changes

* removing duplicate function

* fixing client mock

* removing unused type

* fixing missing brace

* fixing bad field name

* fixing bazel

* updating naming

* fixing bazel

* fixing unit test

* fixing bazel linting

* unhandled err

* fixing gofmt

* simplifying name based on feedback

* using corrected function

* moving default fee recipient and gaslimit to beaconconfig

* missing a few constant changes

* fixing bazel

* fixing more missed default renames

* fixing more constants in tests

* fixing bazel

* adding update proposer setting per epoch

* refactoring to reduce complexity

* adding unit test for proposer settings

* Update validator/client/validator.go

Co-authored-by: terencechain <terence@prysmaticlabs.com>

* trying out renaming based on feedback

* adjusting based on review comments

* making tests more appropriate

* fixing bazel

* updating flag description based on review feedback

* addressing review feedback

* switching to pushing at start of epoch for more time

* adding new unit test and properly throwing error

* switching keys in error to count

* fixing log variable

* resolving conflict

* resolving more conflicts

* adjusting error message

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-06-06 19:32:41 +00:00
james-prysm
f0403afb25 Suggested-Fee-Recipient flag should not override (#10804)
* initial commit

* fixing unit test

* fixing gofmt

* updating usage language

* updating flag description

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-06 16:58:52 +00:00
Potuz
3f309968d4 Only prune in newer finalization (#10831)
* Only prune in newer finalization

* add regression test

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-06 15:25:22 +00:00
Potuz
52acaceb3f Enforce that every node descends from finalized node (#10784)
* Enforce that every node descends from finalized node

* fix test

* fix conflict

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-06-06 14:28:49 +00:00
Nishant Das
5216402f66 Clean Up State Finalizer (#10829) 2022-06-06 09:01:58 +00:00
176 changed files with 6777 additions and 2238 deletions

3
.gitignore vendored
View File

@@ -35,3 +35,6 @@ bin
# p2p metaData
metaData
# execution API authentication
jwt.hex

View File

@@ -2,7 +2,8 @@
[![Build status](https://badge.buildkite.com/b555891daf3614bae4284dcf365b2340cefc0089839526f096.svg?branch=master)](https://buildkite.com/prysmatic-labs/prysm)
[![Go Report Card](https://goreportcard.com/badge/github.com/prysmaticlabs/prysm)](https://goreportcard.com/report/github.com/prysmaticlabs/prysm)
[![Consensus_Spec_Version 1.1.10](https://img.shields.io/badge/Consensus%20Spec%20Version-v1.1.10-blue.svg)](https://github.com/ethereum/consensus-specs/tree/v1.1.10)
[![Consensus_Spec_Version 1.2.0-rc.1](https://img.shields.io/badge/Consensus%20Spec%20Version-v1.2.0.rc.1-blue.svg)](https://github.com/ethereum/consensus-specs/tree/v1.2.0-rc.1)
[![Execution_API_Version 1.0.0-alpha.9](https://img.shields.io/badge/Execution%20API%20Version-v1.0.0.alpha.9-blue.svg)](https://github.com/ethereum/execution-apis/tree/v1.0.0-alpha.9/src/engine)
[![Discord](https://user-images.githubusercontent.com/7288322/34471967-1df7808a-efbb-11e7-9088-ed0b04151291.png)](https://discord.gg/CTYGPUJ)
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the [Ethereum Consensus](https://ethereum.org/en/eth2/) specification, developed by [Prysmatic Labs](https://prysmaticlabs.com). See the [Changelog](https://github.com/prysmaticlabs/prysm/releases) for details of the latest releases and upcoming breaking changes.

View File

@@ -29,7 +29,7 @@ go_test(
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",

View File

@@ -11,7 +11,7 @@ import (
"testing"
"github.com/prysmaticlabs/go-bitfield"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -98,7 +98,7 @@ func TestClient_RegisterValidator(t *testing.T) {
}
reg := &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, fieldparams.EthBurnAddressHex),
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),

View File

@@ -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))
@@ -337,7 +337,7 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
state, blkRoot, err = prepareForkchoiceState(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0)
require.NoError(t, err)
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0)
state, blkRoot, err = prepareForkchoiceState(ctx, 103, [32]byte{'d'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0)
require.NoError(t, err)
require.NoError(t, c.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
state, blkRoot, err = prepareForkchoiceState(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0)
@@ -349,10 +349,19 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
require.DeepEqual(t, []types.Slot{102, 103, 104}, slots)
}
//
// A <- B <- C
// \ \
// \ ---------- E
// ---------- D
func TestService_ChainHeads_DoublyLinkedTree(t *testing.T) {
ctx := context.Background()
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}}
state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 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))
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))
state, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0)
@@ -443,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))
@@ -463,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))
@@ -486,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))
@@ -501,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))
@@ -517,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
@@ -582,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
@@ -646,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

View File

@@ -12,7 +12,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
@@ -270,10 +269,10 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
recipient, err := s.cfg.BeaconDB.FeeRecipientByValidatorID(ctx, proposerID)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
if feeRecipient.String() == fieldparams.EthBurnAddressHex {
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": proposerID,
"burnAddress": fieldparams.EthBurnAddressHex,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +

View File

@@ -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 := &ethpb.Checkpoint{Epoch: 1, Root: tt.finalizedRoot[:]}
fc := &ethpb.Checkpoint{Epoch: 0, Root: tt.finalizedRoot[:]}
service.store.SetFinalizedCheckptAndPayloadHash(fc, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(fc, [32]byte{'b'})
arg := &notifyForkchoiceUpdateArg{
@@ -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)),
@@ -806,7 +809,7 @@ func Test_GetPayloadAttribute(t *testing.T) {
require.NoError(t, err)
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
require.Equal(t, fieldparams.EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient).String())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
// Cache hit, advance state, has fee recipient
@@ -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)

View File

@@ -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

View File

@@ -174,10 +174,8 @@ func TestUpdateHead_MissingJustifiedRoot(t *testing.T) {
service.store.SetJustifiedCheckptAndPayloadHash(&ethpb.Checkpoint{Root: r[:]}, [32]byte{'a'})
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.Checkpoint{}, [32]byte{'b'})
service.store.SetBestJustifiedCheckpt(&ethpb.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 := &ethpb.Checkpoint{
Root: bellatrixBlkRoot[:],
Epoch: 1,
Epoch: 0,
}
service.store.SetFinalizedCheckptAndPayloadHash(fcp, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(fcp, [32]byte{'b'})

View File

@@ -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)),

View File

@@ -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
}
}

View File

@@ -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)),

View File

@@ -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)),

View File

@@ -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)

View File

@@ -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 {
@@ -459,9 +461,12 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, preState, lastBR); err != nil {
return errors.Wrap(err, "could not insert last block in batch to forkchoice")
}
// Prune forkchoice store
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")
// 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.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")
}
}
// Set their optimistic status
@@ -540,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
}
}

View File

@@ -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 {

View File

@@ -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(&ethpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(&ethpb.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,70 @@ 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) {
ctx := context.Background()
beaconDB := testDB.SetupDB(t)
opts := []Option{
WithDatabase(beaconDB),
WithStateGen(stategen.New(beaconDB)),
}
service, err := NewService(ctx, opts...)
require.NoError(t, err)
genesisStateRoot := [32]byte{}
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
wsb, err := wrapper.WrappedSignedBeaconBlock(genesis)
require.NoError(t, err)
assert.NoError(t, beaconDB.SaveBlock(ctx, wsb))
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
service.cfg.ForkChoiceStore = protoarray.New()
wsb, err = wrapper.WrappedSignedBeaconBlock(genesis)
require.NoError(t, err)
service.saveInitSyncBlock(gRoot, wsb)
st, keys := util.DeterministicGenesisState(t, 64)
bState := st.Copy()
var blks []interfaces.SignedBeaconBlock
var blkRoots [][32]byte
var firstState state.BeaconState
for i := 1; i < 128; i++ {
b, err := util.GenerateFullBlock(bState, keys, util.DefaultBlockGenConfig(), types.Slot(i))
require.NoError(t, err)
wsb, err := wrapper.WrappedSignedBeaconBlock(b)
require.NoError(t, err)
bState, err = transition.ExecuteStateTransition(ctx, bState, wsb)
if i == 32 {
firstState = bState.Copy()
}
require.NoError(t, err)
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
wsb, err = wrapper.WrappedSignedBeaconBlock(b)
require.NoError(t, err)
service.saveInitSyncBlock(root, wsb)
wsb, err = wrapper.WrappedSignedBeaconBlock(b)
require.NoError(t, err)
blks = append(blks, wsb)
blkRoots = append(blkRoots, root)
}
for i := 0; i < 32; i++ {
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[i]))
}
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.Checkpoint{Root: blkRoots[31][:], Epoch: 1}, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(&ethpb.Checkpoint{Root: blkRoots[31][:], Epoch: 1}, [32]byte{'b'})
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[31], firstState))
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, firstState, blkRoots[31]))
err = service.onBlockBatch(ctx, blks[32:], blkRoots[32:])
require.NoError(t, err)
}
func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) {
@@ -364,7 +427,7 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) {
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(&ethpb.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)
@@ -411,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) {
@@ -433,7 +496,7 @@ func TestStore_OnBlockBatch_NotifyNewPayload(t *testing.T) {
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.Checkpoint{Root: gRoot[:]}, [32]byte{'a'})
service.store.SetJustifiedCheckptAndPayloadHash(&ethpb.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()
@@ -514,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()
@@ -547,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()
@@ -594,7 +657,7 @@ func TestCachedPreState_CanGetFromStateSummary_ProtoArray(t *testing.T) {
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.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)
@@ -631,7 +694,7 @@ func TestCachedPreState_CanGetFromStateSummary_DoublyLinkedTree(t *testing.T) {
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.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)
@@ -665,7 +728,7 @@ func TestCachedPreState_CanGetFromDB(t *testing.T) {
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
service.store.SetFinalizedCheckptAndPayloadHash(&ethpb.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)
@@ -696,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)
@@ -742,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[:])
@@ -788,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[:])
@@ -835,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[:])
@@ -884,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[:])
@@ -933,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[:])
@@ -1000,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[:])
@@ -1068,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[:])
@@ -1209,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)),
@@ -1300,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)),
@@ -1362,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)),
@@ -1507,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{
@@ -1563,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{
@@ -1582,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{
@@ -1623,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{
@@ -1861,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)),
@@ -1989,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)),
@@ -2040,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{
@@ -2112,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()
}
}
@@ -2120,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)),

View File

@@ -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)),
}

View File

@@ -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"
@@ -206,13 +207,22 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
s.store = store.New(justified, finalized)
var forkChoicer f.ForkChoicer
fRoot := bytesutil.ToBytes32(finalized.Root)
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")

View File

@@ -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(&ethpb.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(&ethpb.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(&ethpb.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(&ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, [32]byte{})

View File

@@ -62,6 +62,7 @@ type ChainService struct {
Genesis time.Time
ForkChoiceStore forkchoice.ForkChoicer
ReceiveBlockMockErr error
OptimisticCheckRootReceived [32]byte
}
// ForkChoicer mocks the same method in the chain service
@@ -447,7 +448,8 @@ func (s *ChainService) IsOptimistic(_ context.Context) (bool, error) {
}
// IsOptimisticForRoot mocks the same method in the chain service.
func (s *ChainService) IsOptimisticForRoot(_ context.Context, _ [32]byte) (bool, error) {
func (s *ChainService) IsOptimisticForRoot(_ context.Context, root [32]byte) (bool, error) {
s.OptimisticCheckRootReceived = root
return s.Optimistic, nil
}

View File

@@ -3,6 +3,8 @@ load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"error.go",
"metric.go",
"option.go",
"service.go",
],
@@ -10,13 +12,19 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//api/client/builder:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/db:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//network:go_default_library",
"//network/authorization:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)

View File

@@ -0,0 +1,7 @@
package builder
import "github.com/pkg/errors"
var (
ErrNotRunning = errors.New("builder is not running")
)

View File

@@ -0,0 +1,37 @@
package builder
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
submitBlindedBlockLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "submit_blinded_block_latency_milliseconds",
Help: "Captures RPC latency for submitting blinded block in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
getHeaderLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "get_header_latency_milliseconds",
Help: "Captures RPC latency for get header in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
getStatusLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "get_status_latency_milliseconds",
Help: "Captures RPC latency for get status in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
registerValidatorLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "register_validator_latency_milliseconds",
Help: "Captures RPC latency for register validator in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
},
)
)

View File

@@ -1,6 +1,7 @@
package builder
import (
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/network"
"github.com/prysmaticlabs/prysm/network/authorization"
@@ -26,6 +27,14 @@ func WithBuilderEndpoints(endpoint string) Option {
}
}
// WithDatabase sets the database for the beacon chain builder service.
func WithDatabase(database db.HeadAccessDatabase) Option {
return func(s *Service) error {
s.cfg.beaconDB = database
return nil
}
}
func covertEndPoint(ep string) network.Endpoint {
return network.Endpoint{
Url: ep,

View File

@@ -2,26 +2,33 @@ package builder
import (
"context"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/api/client/builder"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/network"
v1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
)
// BlockBuilder defines the interface for interacting with the block builder
type BlockBuilder interface {
SubmitBlindedBlock(ctx context.Context, block *ethpb.SignedBlindedBeaconBlockBellatrix) (*v1.ExecutionPayload, error)
GetHeader(ctx context.Context, slot types.Slot, parentHash [32]byte, pubKey [48]byte) (*ethpb.SignedBuilderBid, error)
Status() error
Status(ctx context.Context) error
RegisterValidator(ctx context.Context, reg *ethpb.SignedValidatorRegistrationV1) error
}
// config defines a config struct for dependencies into the service.
type config struct {
builderEndpoint network.Endpoint
beaconDB db.HeadAccessDatabase
headFetcher blockchain.HeadFetcher
}
// Service defines a service that provides a client for interacting with the beacon chain and MEV relay network.
@@ -56,22 +63,59 @@ func (*Service) Stop() error {
return nil
}
// SubmitBlindedBlock is currently a stub.
func (*Service) SubmitBlindedBlock(context.Context, *ethpb.SignedBlindedBeaconBlockBellatrix) (*v1.ExecutionPayload, error) {
return nil, errors.New("not implemented")
// SubmitBlindedBlock submits a blinded block to the builder relay network.
func (s *Service) SubmitBlindedBlock(ctx context.Context, b *ethpb.SignedBlindedBeaconBlockBellatrix) (*v1.ExecutionPayload, error) {
ctx, span := trace.StartSpan(ctx, "builder.SubmitBlindedBlock")
defer span.End()
start := time.Now()
defer func() {
submitBlindedBlockLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
return s.c.SubmitBlindedBlock(ctx, b)
}
// GetHeader is currently a stub.
func (*Service) GetHeader(context.Context, types.Slot, [32]byte, [48]byte) (*ethpb.SignedBuilderBid, error) {
return nil, errors.New("not implemented")
// GetHeader retrieves the header for a given slot and parent hash from the builder relay network.
func (s *Service) GetHeader(ctx context.Context, slot types.Slot, parentHash [32]byte, pubKey [48]byte) (*ethpb.SignedBuilderBid, error) {
ctx, span := trace.StartSpan(ctx, "builder.GetHeader")
defer span.End()
start := time.Now()
defer func() {
getHeaderLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
return s.c.GetHeader(ctx, slot, parentHash, pubKey)
}
// Status is currently a stub.
func (*Service) Status() error {
return errors.New("not implemented")
// Status retrieves the status of the builder relay network.
func (s *Service) Status(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "builder.Status")
defer span.End()
start := time.Now()
defer func() {
getStatusLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
return s.c.Status(ctx)
}
// RegisterValidator is currently a stub.
func (*Service) RegisterValidator(context.Context, *ethpb.SignedValidatorRegistrationV1) error {
return errors.New("not implemented")
// RegisterValidator registers a validator with the builder relay network.
// It also saves the registration object to the DB.
func (s *Service) RegisterValidator(ctx context.Context, reg *ethpb.SignedValidatorRegistrationV1) error {
ctx, span := trace.StartSpan(ctx, "builder.RegisterValidator")
defer span.End()
start := time.Now()
defer func() {
registerValidatorLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
idx, exists := s.cfg.headFetcher.HeadPublicKeyToValidatorIndex(bytesutil.ToBytes48(reg.Message.Pubkey))
if !exists {
return nil // If the pubkey is not found, it is not a validator. Do nothing.
}
if err := s.c.RegisterValidator(ctx, reg); err != nil {
return errors.Wrap(err, "could not register validator")
}
return s.cfg.beaconDB.SaveRegistrationsByValidatorIDs(ctx, []types.ValidatorIndex{idx}, []*ethpb.ValidatorRegistrationV1{reg.Message})
}

View File

@@ -12,7 +12,6 @@ import (
"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/runtime/version"
"github.com/prysmaticlabs/prysm/time/slots"
"google.golang.org/protobuf/proto"
)
@@ -75,16 +74,10 @@ func ProcessProposerSlashing(
return nil, errors.Wrap(err, "could not verify proposer slashing")
}
cfg := params.BeaconConfig()
var slashingQuotient uint64
switch {
case beaconState.Version() == version.Phase0:
slashingQuotient = cfg.MinSlashingPenaltyQuotient
case beaconState.Version() == version.Altair:
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
case beaconState.Version() == version.Bellatrix:
slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix
default:
return nil, errors.New("unknown state version")
slashingQuotient, err := beaconState.MinSlashingPenaltyQuotient()
if err != nil {
return nil, err
}
beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, slashingQuotient, cfg.ProposerRewardQuotient)
if err != nil {

View File

@@ -23,14 +23,14 @@ type ReadOnlyDatabase interface {
Block(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error)
Blocks(ctx context.Context, f *filters.QueryFilter) ([]interfaces.SignedBeaconBlock, [][32]byte, error)
BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error)
BlocksBySlot(ctx context.Context, slot types.Slot) (bool, []interfaces.SignedBeaconBlock, error)
BlocksBySlot(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error)
BlockRootsBySlot(ctx context.Context, slot types.Slot) (bool, [][32]byte, error)
HasBlock(ctx context.Context, blockRoot [32]byte) bool
GenesisBlock(ctx context.Context) (interfaces.SignedBeaconBlock, error)
GenesisBlockRoot(ctx context.Context) ([32]byte, error)
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
FinalizedChildBlock(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error)
HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error)
HighestRootsBelowSlot(ctx context.Context, slot types.Slot) (types.Slot, [][32]byte, error)
// State related methods.
State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
StateOrError(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
@@ -86,6 +86,7 @@ type NoHeadAccessDatabase interface {
RunMigrations(ctx context.Context) error
// Fee reicipients operations.
SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []types.ValidatorIndex, addrs []common.Address) error
SaveRegistrationsByValidatorIDs(ctx context.Context, ids []types.ValidatorIndex, regs []*ethpb.ValidatorRegistrationV1) error
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint types.Slot) error
}

View File

@@ -185,17 +185,19 @@ func (s *Store) HasBlock(ctx context.Context, blockRoot [32]byte) bool {
}
// BlocksBySlot retrieves a list of beacon blocks and its respective roots by slot.
func (s *Store) BlocksBySlot(ctx context.Context, slot types.Slot) (bool, []interfaces.SignedBeaconBlock, error) {
func (s *Store) BlocksBySlot(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.BlocksBySlot")
defer span.End()
blocks := make([]interfaces.SignedBeaconBlock, 0)
blocks := make([]interfaces.SignedBeaconBlock, 0)
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
keys := blockRootsBySlot(ctx, tx, slot)
for i := 0; i < len(keys); i++ {
encoded := bkt.Get(keys[i])
roots, err := blockRootsBySlot(ctx, tx, slot)
if err != nil {
return errors.Wrap(err, "could not retrieve blocks by slot")
}
for _, r := range roots {
encoded := bkt.Get(r[:])
blk, err := unmarshalBlock(ctx, encoded)
if err != nil {
return err
@@ -204,7 +206,7 @@ func (s *Store) BlocksBySlot(ctx context.Context, slot types.Slot) (bool, []inte
}
return nil
})
return len(blocks) > 0, blocks, err
return blocks, err
}
// BlockRootsBySlot retrieves a list of beacon block roots by slot
@@ -213,11 +215,9 @@ func (s *Store) BlockRootsBySlot(ctx context.Context, slot types.Slot) (bool, []
defer span.End()
blockRoots := make([][32]byte, 0)
err := s.db.View(func(tx *bolt.Tx) error {
keys := blockRootsBySlot(ctx, tx, slot)
for i := 0; i < len(keys); i++ {
blockRoots = append(blockRoots, bytesutil.ToBytes32(keys[i]))
}
return nil
var err error
blockRoots, err = blockRootsBySlot(ctx, tx, slot)
return err
})
if err != nil {
return false, nil, errors.Wrap(err, "could not retrieve block roots by slot")
@@ -398,14 +398,17 @@ func (s *Store) SaveBackfillBlockRoot(ctx context.Context, blockRoot [32]byte) e
})
}
// HighestSlotBlocksBelow returns the block with the highest slot below the input slot from the db.
func (s *Store) HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.HighestSlotBlocksBelow")
// HighestRootsBelowSlot returns roots from the database slot index from the highest slot below the input slot.
// The slot value at the beginning of the return list is the slot where the roots were found. This is helpful so that
// calling code can make decisions based on the slot without resolving the blocks to discover their slot (for instance
// checking which root is canonical in fork choice, which operates purely on roots,
// then if no canonical block is found, continuing to search through lower slots).
func (s *Store) HighestRootsBelowSlot(ctx context.Context, slot types.Slot) (fs types.Slot, roots [][32]byte, err error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.HighestRootsBelowSlot")
defer span.End()
var root [32]byte
sk := bytesutil.Uint64ToBytesBigEndian(uint64(slot))
err := s.db.View(func(tx *bolt.Tx) error {
err = s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blockSlotIndicesBucket)
c := bkt.Cursor()
// The documentation for Seek says:
@@ -430,34 +433,28 @@ func (s *Store) HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]
if r == nil {
continue
}
bs := bytesutil.BytesToSlotBigEndian(sl)
fs = bytesutil.BytesToSlotBigEndian(sl)
// Iterating through the index using .Prev will move from higher to lower, so the first key we find behind
// the requested slot must be the highest block below that slot.
if slot > bs {
root = bytesutil.ToBytes32(r)
break
if slot > fs {
roots, err = splitRoots(r)
if err != nil {
return errors.Wrapf(err, "error parsing packed roots %#x", r)
}
return nil
}
}
return nil
})
if err != nil {
return nil, err
return 0, nil, err
}
if len(roots) == 0 || (len(roots) == 1 && roots[0] == params.BeaconConfig().ZeroHash) {
gr, err := s.GenesisBlockRoot(ctx)
return 0, [][32]byte{gr}, err
}
var blk interfaces.SignedBeaconBlock
if root != params.BeaconConfig().ZeroHash {
blk, err = s.Block(ctx, root)
if err != nil {
return nil, err
}
}
if blk == nil || blk.IsNil() {
blk, err = s.GenesisBlock(ctx)
if err != nil {
return nil, err
}
}
return []interfaces.SignedBeaconBlock{blk}, nil
return fs, roots, nil
}
// FeeRecipientByValidatorID returns the fee recipient for a validator id.
@@ -681,21 +678,22 @@ func blockRootsBySlotRange(
}
// blockRootsBySlot retrieves the block roots by slot
func blockRootsBySlot(ctx context.Context, tx *bolt.Tx, slot types.Slot) [][]byte {
func blockRootsBySlot(ctx context.Context, tx *bolt.Tx, slot types.Slot) ([][32]byte, error) {
_, span := trace.StartSpan(ctx, "BeaconDB.blockRootsBySlot")
defer span.End()
roots := make([][]byte, 0)
bkt := tx.Bucket(blockSlotIndicesBucket)
key := bytesutil.SlotToBytesBigEndian(slot)
c := bkt.Cursor()
k, v := c.Seek(key)
if k != nil && bytes.Equal(k, key) {
for i := 0; i < len(v); i += 32 {
roots = append(roots, v[i:i+32])
r, err := splitRoots(v)
if err != nil {
return nil, errors.Wrapf(err, "corrupt value in block slot index for slot=%d", slot)
}
return r, nil
}
return roots
return [][32]byte{}, nil
}
// createBlockIndicesFromBlock takes in a beacon block and returns

View File

@@ -517,18 +517,32 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
require.NoError(t, db.SaveBlock(ctx, block2))
require.NoError(t, db.SaveBlock(ctx, block3))
highestAt, err := db.HighestSlotBlocksBelow(ctx, 2)
_, roots, err := db.HighestRootsBelowSlot(ctx, 2)
require.NoError(t, err)
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
assert.Equal(t, true, proto.Equal(block1.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", block1, highestAt[0])
highestAt, err = db.HighestSlotBlocksBelow(ctx, 11)
assert.Equal(t, false, len(roots) <= 0, "Got empty highest at slice")
require.Equal(t, 1, len(roots))
root := roots[0]
b, err := db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
assert.Equal(t, true, proto.Equal(block2.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", block2, highestAt[0])
highestAt, err = db.HighestSlotBlocksBelow(ctx, 101)
assert.Equal(t, true, proto.Equal(block1.Proto(), b.Proto()), "Wanted: %v, received: %v", block1, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 11)
require.NoError(t, err)
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
assert.Equal(t, true, proto.Equal(block3.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", block3, highestAt[0])
assert.Equal(t, false, len(roots) <= 0, "Got empty highest at slice")
require.Equal(t, 1, len(roots))
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block2.Proto(), b.Proto()), "Wanted: %v, received: %v", block2, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 101)
require.NoError(t, err)
assert.Equal(t, false, len(roots) <= 0, "Got empty highest at slice")
require.Equal(t, 1, len(roots))
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block3.Proto(), b.Proto()), "Wanted: %v, received: %v", block3, b)
})
}
}
@@ -549,15 +563,29 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, block1))
highestAt, err := db.HighestSlotBlocksBelow(ctx, 2)
_, roots, err := db.HighestRootsBelowSlot(ctx, 2)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block1.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", block1, highestAt[0])
highestAt, err = db.HighestSlotBlocksBelow(ctx, 1)
require.Equal(t, 1, len(roots))
root := roots[0]
b, err := db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", genesisBlock, highestAt[0])
highestAt, err = db.HighestSlotBlocksBelow(ctx, 0)
assert.Equal(t, true, proto.Equal(block1.Proto(), b.Proto()), "Wanted: %v, received: %v", block1, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 1)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), highestAt[0].Proto()), "Wanted: %v, received: %v", genesisBlock, highestAt[0])
require.Equal(t, 1, len(roots))
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), b.Proto()), "Wanted: %v, received: %v", genesisBlock, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 0)
require.NoError(t, err)
require.Equal(t, 1, len(roots))
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), b.Proto()), "Wanted: %v, received: %v", genesisBlock, b)
})
}
}
@@ -638,22 +666,21 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
r3, err := b3.Block().HashTreeRoot()
require.NoError(t, err)
hasBlocks, retrievedBlocks, err := db.BlocksBySlot(ctx, 1)
retrievedBlocks, err := db.BlocksBySlot(ctx, 1)
require.NoError(t, err)
assert.Equal(t, 0, len(retrievedBlocks), "Unexpected number of blocks received, expected none")
assert.Equal(t, false, hasBlocks, "Expected no blocks")
hasBlocks, retrievedBlocks, err = db.BlocksBySlot(ctx, 20)
retrievedBlocks, err = db.BlocksBySlot(ctx, 20)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(b1.Proto(), retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b1, retrievedBlocks[0])
assert.Equal(t, true, hasBlocks, "Expected to have blocks")
hasBlocks, retrievedBlocks, err = db.BlocksBySlot(ctx, 100)
assert.Equal(t, true, len(retrievedBlocks) > 0, "Expected to have blocks")
retrievedBlocks, err = db.BlocksBySlot(ctx, 100)
require.NoError(t, err)
if len(retrievedBlocks) != 2 {
t.Fatalf("Expected 2 blocks, received %d blocks", len(retrievedBlocks))
}
assert.Equal(t, true, proto.Equal(b2.Proto(), retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b2, retrievedBlocks[0])
assert.Equal(t, true, proto.Equal(b3.Proto(), retrievedBlocks[1].Proto()), "Wanted: %v, received: %v", b3, retrievedBlocks[1])
assert.Equal(t, true, hasBlocks, "Expected to have blocks")
assert.Equal(t, true, len(retrievedBlocks) > 0, "Expected to have blocks")
hasBlockRoots, retrievedBlockRoots, err := db.BlockRootsBySlot(ctx, 1)
require.NoError(t, err)

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
@@ -99,3 +101,16 @@ func deleteValueForIndices(ctx context.Context, indicesByBucket map[string][]byt
}
return nil
}
var errMisalignedRootList = errors.New("incorrectly packed root list, length is not a multiple of 32")
func splitRoots(b []byte) ([][32]byte, error) {
rl := make([][32]byte, 0)
if len(b)%32 != 0 {
return nil, errors.Wrapf(errMisalignedRootList, "root list len=%d", len(b))
}
for s, f := 0, 32; f <= len(b); s, f = f, f+32 {
rl = append(rl, bytesutil.ToBytes32(b[s:f]))
}
return rl, nil
}

View File

@@ -138,3 +138,60 @@ func Test_deleteValueForIndices(t *testing.T) {
})
}
}
func testPack(bs [][32]byte) []byte {
r := make([]byte, 0)
for _, b := range bs {
r = append(r, b[:]...)
}
return r
}
func TestSplitRoots(t *testing.T) {
bt := make([][32]byte, 0)
for _, x := range []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {
var b [32]byte
for i := 0; i < 32; i++ {
b[i] = x
}
bt = append(bt, b)
}
cases := []struct {
name string
b []byte
expect [][32]byte
err error
}{
{
name: "misaligned",
b: make([]byte, 61),
err: errMisalignedRootList,
},
{
name: "happy",
b: testPack(bt[0:5]),
expect: bt[0:5],
},
{
name: "single",
b: testPack([][32]byte{bt[0]}),
expect: [][32]byte{bt[0]},
},
{
name: "empty",
b: []byte{},
expect: [][32]byte{},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
r, err := splitRoots(c.b)
if c.err != nil {
require.ErrorIs(t, err, c.err)
return
}
require.NoError(t, err)
require.DeepEqual(t, c.expect, r)
})
}
}

View File

@@ -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

View File

@@ -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
}
@@ -435,6 +440,8 @@ func (f *ForkChoice) CommonAncestorRoot(ctx context.Context, r1 [32]byte, r2 [32
if n1.slot > n2.slot {
n1 = n1.parent
// Reaches the end of the tree and unable to find common ancestor.
// This should not happen at runtime as the finalized
// node has to be a common ancestor
if n1 == nil {
return [32]byte{}, forkchoice.ErrUnknownCommonAncestor
}
@@ -471,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
}
}

View File

@@ -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 := &ethpb.Checkpoint{Root: jr[:], Epoch: 3}
fc := &ethpb.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) {
@@ -535,9 +539,17 @@ func TestStore_CommonAncestor(t *testing.T) {
require.ErrorIs(t, err, ErrNilNode)
_, err = f.CommonAncestorRoot(ctx, [32]byte{'z'}, [32]byte{'a'})
require.ErrorIs(t, err, ErrNilNode)
state, blkRoot, err = prepareForkchoiceState(ctx, 100, [32]byte{'y'}, [32]byte{'z'}, [32]byte{}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
n := &Node{
slot: 100,
root: [32]byte{'y'},
justifiedEpoch: 1,
unrealizedJustifiedEpoch: 1,
finalizedEpoch: 1,
unrealizedFinalizedEpoch: 1,
optimistic: true,
}
f.store.nodeByRoot[[32]byte{'y'}] = n
// broken link
_, err = f.CommonAncestorRoot(ctx, [32]byte{'y'}, [32]byte{'a'})
require.ErrorIs(t, err, forkchoice.ErrUnknownCommonAncestor)
@@ -554,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: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
})
for i := uint64(2); i < 11; i++ {
blk := util.NewBeaconBlock()
blk.Block.Slot = types.Slot(i)
@@ -563,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: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
})
root, err = blk.Block.HashTreeRoot()
require.NoError(t, err)
}

View File

@@ -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")
}

View File

@@ -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")
})

View File

@@ -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.
@@ -125,19 +125,21 @@ func (s *Store) insert(ctx context.Context,
s.nodeByPayload[payloadHash] = n
s.nodeByRoot[root] = n
if parent != nil {
if parent == nil {
if s.treeRootNode == nil {
s.treeRootNode = n
s.headNode = n
} else {
return errInvalidParentRoot
}
} 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
}
}
// Set the node as root if the store was empty
if s.treeRootNode == nil {
s.treeRootNode = n
s.headNode = n
}
// Update metrics.
processedBlockCount.Inc()
nodeCount.Set(float64(len(s.nodeByRoot)))

View File

@@ -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")

View File

@@ -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.

View File

@@ -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
}
}
}

View File

@@ -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(&ethpb.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(&ethpb.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)
}

View File

@@ -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")
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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")
})

View File

@@ -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
}
}

View File

@@ -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 := &ethpb.Checkpoint{Root: jr[:], Epoch: 3}
fc := &ethpb.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: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
FinalizedCheckpoint: &ethpb.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: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1, Root: params.BeaconConfig().ZeroHash[:]},
})
root, err = blk.Block.HashTreeRoot()
require.NoError(t, err)
}

View File

@@ -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.

View File

@@ -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
}
}
}

View File

@@ -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(&ethpb.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(&ethpb.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)

View File

@@ -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")
}

View File

@@ -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",
],
)

View File

@@ -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
}

View File

@@ -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()
}
}

View File

@@ -142,5 +142,7 @@ func (s *Service) logTtdStatus(ctx context.Context, ttd *uint256.Int) (bool, err
"latestDifficulty": latestTtd.String(),
"terminalDifficulty": ttd.ToBig().String(),
}).Info("terminal difficulty has not been reached yet")
totalTerminalDifficulty.Set(float64(latestTtd.Uint64()))
return false, nil
}

View File

@@ -6,25 +6,29 @@ import (
)
var (
totalTerminalDifficulty = promauto.NewGauge(prometheus.GaugeOpts{
Name: "total_terminal_difficulty",
Help: "The total terminal difficulty of the execution chain before merge",
})
newPayloadLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "new_payload_v1_latency_milliseconds",
Help: "Captures RPC latency for newPayloadV1 in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
Buckets: []float64{25, 50, 100, 200, 500, 1000, 2000, 4000},
},
)
getPayloadLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "get_payload_v1_latency_milliseconds",
Help: "Captures RPC latency for getPayloadV1 in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
Buckets: []float64{25, 50, 100, 200, 500, 1000, 2000, 4000},
},
)
forkchoiceUpdatedLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "forkchoice_updated_v1_latency_milliseconds",
Help: "Captures RPC latency for forkchoiceUpdatedV1 in milliseconds",
Buckets: []float64{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000},
Buckets: []float64{25, 50, 100, 200, 500, 1000, 2000, 4000},
},
)
)

View File

@@ -70,10 +70,14 @@ func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*eth
if err != nil {
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity slot: %v", err)
}
cbr, cb, err := bs.CanonicalHistory.BlockForSlot(ctx, wsSlot)
cbr, err := bs.CanonicalHistory.BlockRootForSlot(ctx, wsSlot)
if err != nil {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("could not find highest block below slot %d", wsSlot))
}
cb, err := bs.BeaconDB.Block(ctx, cbr)
if err != nil {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("block with root %#x from slot index %d not found in db", cbr, wsSlot))
}
stateRoot := bytesutil.ToBytes32(cb.Block().StateRoot())
log.Printf("weak subjectivity checkpoint reported as epoch=%d, block root=%#x, state root=%#x", wsEpoch, cbr, stateRoot)
return &ethpbv1.WeakSubjectivityResponse{
@@ -150,7 +154,7 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader
if req.Slot != nil {
slot = *req.Slot
}
_, blks, err = bs.BeaconDB.BlocksBySlot(ctx, slot)
blks, err = bs.BeaconDB.BlocksBySlot(ctx, slot)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not retrieve blocks for slot %d: %v", req.Slot, err)
}
@@ -754,7 +758,7 @@ func (bs *Server) blockFromBlockID(ctx context.Context, blockId []byte) (interfa
e := newBlockIdParseError(err)
return nil, &e
}
_, blks, err := bs.BeaconDB.BlocksBySlot(ctx, types.Slot(slot))
blks, err := bs.BeaconDB.BlocksBySlot(ctx, types.Slot(slot))
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve blocks for slot %d", slot)
}

View File

@@ -1527,8 +1527,8 @@ func TestServer_GetBlockSSZ(t *testing.T) {
},
}
ok, blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, ok)
blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, len(blocks) > 0)
require.NoError(t, err)
sszBlock, err := blocks[0].MarshalSSZ()
require.NoError(t, err)
@@ -1567,8 +1567,8 @@ func TestServer_GetBlockSSZV2(t *testing.T) {
},
}
ok, blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, ok)
blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, len(blocks) > 0)
require.NoError(t, err)
sszBlock, err := blocks[0].MarshalSSZ()
require.NoError(t, err)
@@ -1606,8 +1606,8 @@ func TestServer_GetBlockSSZV2(t *testing.T) {
},
}
ok, blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, ok)
blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, len(blocks) > 0)
require.NoError(t, err)
sszBlock, err := blocks[0].MarshalSSZ()
require.NoError(t, err)
@@ -1645,8 +1645,8 @@ func TestServer_GetBlockSSZV2(t *testing.T) {
},
}
ok, blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, ok)
blocks, err := beaconDB.BlocksBySlot(ctx, 30)
require.Equal(t, true, len(blocks) > 0)
require.NoError(t, err)
sszBlock, err := blocks[0].MarshalSSZ()
require.NoError(t, err)

View File

@@ -1,6 +1,7 @@
package helpers
import (
"bytes"
"context"
"strconv"
@@ -9,6 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
"github.com/prysmaticlabs/prysm/config/params"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -40,12 +42,15 @@ func ValidateSync(ctx context.Context, syncChecker sync.Checker, headFetcher blo
// IsOptimistic checks whether the latest block header of the passed in beacon state is the header of an optimistic block.
func IsOptimistic(ctx context.Context, st state.BeaconState, optimisticSyncFetcher blockchain.OptimisticModeFetcher) (bool, error) {
root, err := st.HashTreeRoot(ctx)
if err != nil {
return false, errors.Wrap(err, "could not get state root")
}
header := st.LatestBlockHeader()
header.StateRoot = root[:]
// This happens when the block at the state's slot is not missing.
if bytes.Equal(header.StateRoot, params.BeaconConfig().ZeroHash[:]) {
root, err := st.HashTreeRoot(ctx)
if err != nil {
return false, errors.Wrap(err, "could not get state root")
}
header.StateRoot = root[:]
}
headRoot, err := header.HashTreeRoot()
if err != nil {
return false, errors.Wrap(err, "could not get header root")

View File

@@ -69,4 +69,29 @@ func TestIsOptimistic(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, false, o)
})
t.Run("zero state root", func(t *testing.T) {
zeroRootSt, err := util.NewBeaconState()
require.NoError(t, err)
h := zeroRootSt.LatestBlockHeader()
h.StateRoot = make([]byte, 32)
require.NoError(t, zeroRootSt.SetLatestBlockHeader(h))
mockOptSyncFetcher := &chainmock.ChainService{}
_, err = IsOptimistic(ctx, st, mockOptSyncFetcher)
require.NoError(t, err)
assert.DeepEqual(
t,
[32]byte{0xfc, 0x0, 0xe9, 0x6d, 0xb, 0x8b, 0x2, 0x2f, 0x61, 0xeb, 0x92, 0x10, 0xfd, 0x80, 0x84, 0x2b, 0x26, 0x61, 0xdc, 0x94, 0x5f, 0x7a, 0xf0, 0x0, 0xbc, 0x38, 0x6, 0x38, 0x71, 0x95, 0x43, 0x1},
mockOptSyncFetcher.OptimisticCheckRootReceived,
)
})
t.Run("non-zero state root", func(t *testing.T) {
mockOptSyncFetcher := &chainmock.ChainService{}
_, err = IsOptimistic(ctx, st, mockOptSyncFetcher)
require.NoError(t, err)
assert.DeepEqual(
t,
[32]byte{0xfc, 0x0, 0xe9, 0x6d, 0xb, 0x8b, 0x2, 0x2f, 0x61, 0xeb, 0x92, 0x10, 0xfd, 0x80, 0x84, 0x2b, 0x26, 0x61, 0xdc, 0x94, 0x5f, 0x7a, 0xf0, 0x0, 0xbc, 0x38, 0x6, 0x38, 0x71, 0x95, 0x43, 0x1},
mockOptSyncFetcher.OptimisticCheckRootReceived,
)
})
}

View File

@@ -209,11 +209,11 @@ func (bs *Server) listBlocksForRoot(ctx context.Context, _ *ethpb.ListBlocksRequ
// listBlocksForSlot retrieves all blocks for the provided slot.
func (bs *Server) listBlocksForSlot(ctx context.Context, req *ethpb.ListBlocksRequest, q *ethpb.ListBlocksRequest_Slot) ([]blockContainer, int, string, error) {
hasBlocks, blks, err := bs.BeaconDB.BlocksBySlot(ctx, q.Slot)
blks, err := bs.BeaconDB.BlocksBySlot(ctx, q.Slot)
if err != nil {
return nil, 0, strconv.Itoa(0), status.Errorf(codes.Internal, "Could not retrieve blocks for slot %d: %v", q.Slot, err)
}
if !hasBlocks {
if len(blks) == 0 {
return []blockContainer{}, 0, strconv.Itoa(0), nil
}
@@ -393,6 +393,10 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
if err != nil {
return nil, status.Error(codes.Internal, "Could not get head block")
}
optimisticStatus, err := bs.OptimisticModeFetcher.IsOptimistic(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get optimistic status")
}
if err := wrapper.BeaconBlockIsNil(headBlock); err != nil {
return nil, status.Errorf(codes.NotFound, "Head block of chain was nil: %v", err)
}
@@ -474,5 +478,6 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
PreviousJustifiedSlot: pjSlot,
PreviousJustifiedEpoch: prevJustifiedCheckpoint.Epoch,
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
OptimisticStatus: optimisticStatus,
}, nil
}

View File

@@ -426,6 +426,7 @@ func TestServer_GetChainHead_NoGenesis(t *testing.T) {
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
}
_, err = bs.GetChainHead(context.Background(), nil)
require.ErrorContains(t, "Could not get genesis block", err)
@@ -461,6 +462,7 @@ func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
}
_, err = bs.GetChainHead(context.Background(), nil)
@@ -469,7 +471,8 @@ func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
func TestServer_GetChainHead_NoHeadBlock(t *testing.T) {
bs := &Server{
HeadFetcher: &chainMock.ChainService{Block: nil},
HeadFetcher: &chainMock.ChainService{Block: nil},
OptimisticModeFetcher: &chainMock.ChainService{},
}
_, err := bs.GetChainHead(context.Background(), nil)
assert.ErrorContains(t, "Head block of chain was nil", err)
@@ -531,8 +534,9 @@ func TestServer_GetChainHead(t *testing.T) {
wsb, err = wrapper.WrappedSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
OptimisticModeFetcher: &chainMock.ChainService{},
FinalizationFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
@@ -550,6 +554,7 @@ func TestServer_GetChainHead(t *testing.T) {
assert.DeepEqual(t, pjRoot[:], head.PreviousJustifiedBlockRoot, "Unexpected PreviousJustifiedBlockRoot")
assert.DeepEqual(t, jRoot[:], head.JustifiedBlockRoot, "Unexpected JustifiedBlockRoot")
assert.DeepEqual(t, fRoot[:], head.FinalizedBlockRoot, "Unexpected FinalizedBlockRoot")
assert.Equal(t, false, head.OptimisticStatus)
}
func TestServer_StreamChainHead_ContextCanceled(t *testing.T) {
@@ -645,6 +650,7 @@ func TestServer_StreamChainHead_OnHeadUpdated(t *testing.T) {
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
}
exitRoutine := make(chan bool)
ctrl := gomock.NewController(t)

View File

@@ -47,4 +47,5 @@ type Server struct {
SyncChecker sync.Checker
ReplayerBuilder stategen.ReplayerBuilder
HeadUpdater blockchain.HeadUpdater
OptimisticModeFetcher blockchain.OptimisticModeFetcher
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -127,10 +127,10 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
case errors.As(err, kv.ErrNotFoundFeeRecipient):
// If fee recipient is not found in DB and not set from beacon node CLI,
// use the burn address.
if feeRecipient.String() == fieldparams.EthBurnAddressHex {
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": vIdx,
"burnAddress": fieldparams.EthBurnAddressHex,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +

View File

@@ -268,6 +268,7 @@ func (s *Service) Start() {
AttestationsPool: s.cfg.AttestationsPool,
SlashingsPool: s.cfg.SlashingsPool,
HeadUpdater: s.cfg.HeadUpdater,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
HeadFetcher: s.cfg.HeadFetcher,
FinalizationFetcher: s.cfg.FinalizationFetcher,
CanonicalFetcher: s.cfg.CanonicalFetcher,

View File

@@ -292,11 +292,11 @@ func (p *StateProvider) stateRootBySlot(ctx context.Context, slot types.Slot) ([
if slot > currentSlot {
return nil, errors.New("slot cannot be in the future")
}
found, blks, err := p.BeaconDB.BlocksBySlot(ctx, slot)
blks, err := p.BeaconDB.BlocksBySlot(ctx, slot)
if err != nil {
return nil, errors.Wrap(err, "could not get blocks")
}
if !found {
if len(blks) == 0 {
return nil, errors.New("no block exists")
}
if len(blks) != 1 {

View File

@@ -15,7 +15,6 @@ go_library(
"//beacon-chain/state/types:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",

View File

@@ -12,7 +12,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
pmath "github.com/prysmaticlabs/prysm/math"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -353,17 +352,7 @@ func handlePendingAttestationSlice(val []*ethpb.PendingAttestation, indices []ui
// handleBalanceSlice returns the root of a slice of validator balances.
func handleBalanceSlice(val, indices []uint64, convertAll bool) ([][32]byte, error) {
if convertAll {
balancesMarshaling := make([][]byte, len(val))
for i, b := range val {
balanceBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(balanceBuf, b)
balancesMarshaling[i] = balanceBuf
}
balancesChunks, err := ssz.PackByChunk(balancesMarshaling)
if err != nil {
return [][32]byte{}, errors.Wrap(err, "could not pack balances into chunks")
}
return balancesChunks, nil
return stateutil.PackUint64IntoChunks(val)
}
if len(val) > 0 {
numOfElems, err := types.Balances.ElemsInChunk()

View File

@@ -26,6 +26,7 @@ type BeaconState interface {
// SpecParametersProvider provides fork-specific configuration parameters as
// defined in the consensus specification for the beacon chain.
type SpecParametersProvider interface {
MinSlashingPenaltyQuotient() (uint64, error)
InactivityPenaltyQuotient() (uint64, error)
ProportionalSlashingMultiplier() (uint64, error)
}

View File

@@ -28,3 +28,15 @@ func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
}
return 0, errNotSupported("InactivityPenaltyQuotient()", b.version)
}
func (b *BeaconState) MinSlashingPenaltyQuotient() (uint64, error) {
switch b.version {
case version.Bellatrix:
return params.BeaconConfig().MinSlashingPenaltyQuotientBellatrix, nil
case version.Altair:
return params.BeaconConfig().MinSlashingPenaltyQuotientAltair, nil
case version.Phase0:
return params.BeaconConfig().MinSlashingPenaltyQuotient, nil
}
return 0, errNotSupported("MinSlashingPenaltyQuotient()", b.version)
}

View File

@@ -199,6 +199,8 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState,
b.sharedFieldReferences[nativetypes.CurrentEpochAttestations] = stateutil.NewRef(1)
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -287,6 +289,8 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS
b.sharedFieldReferences[nativetypes.InactivityScores] = stateutil.NewRef(1) // New in Altair.
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -377,6 +381,8 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B
b.sharedFieldReferences[nativetypes.LatestExecutionPayloadHeader] = stateutil.NewRef(1) // New in Bellatrix.
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -495,31 +501,7 @@ func (b *BeaconState) Copy() state.BeaconState {
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := range b.dirtyFields {
delete(b.dirtyFields, i)
}
for i := range b.rebuildTrie {
delete(b.rebuildTrie, i)
}
for i := range b.dirtyIndices {
delete(b.dirtyIndices, i)
}
for i := range b.sharedFieldReferences {
delete(b.sharedFieldReferences, i)
}
for i := range b.stateFieldLeaves {
delete(b.stateFieldLeaves, i)
}
state.StateCount.Sub(1)
})
runtime.SetFinalizer(dst, finalizerCleanup)
return dst
}
@@ -792,3 +774,29 @@ func (b *BeaconState) resetFieldTrie(index nativetypes.FieldIndex, elements inte
b.dirtyIndices[index] = []uint64{}
return nil
}
func finalizerCleanup(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := range b.dirtyFields {
delete(b.dirtyFields, i)
}
for i := range b.rebuildTrie {
delete(b.rebuildTrie, i)
}
for i := range b.dirtyIndices {
delete(b.dirtyIndices, i)
}
for i := range b.sharedFieldReferences {
delete(b.sharedFieldReferences, i)
}
for i := range b.stateFieldLeaves {
delete(b.stateFieldLeaves, i)
}
state.StateCount.Sub(1)
}

View File

@@ -46,73 +46,58 @@ func (c *CanonicalHistory) ReplayerForSlot(target types.Slot) Replayer {
return &stateReplayer{chainer: c, method: forSlot, target: target}
}
func (c *CanonicalHistory) BlockForSlot(ctx context.Context, target types.Slot) ([32]byte, interfaces.SignedBeaconBlock, error) {
currentSlot := c.cs.CurrentSlot()
if target > currentSlot {
return [32]byte{}, nil, errors.Wrap(ErrFutureSlotRequested, fmt.Sprintf("requested=%d, current=%d", target, currentSlot))
func (c *CanonicalHistory) BlockRootForSlot(ctx context.Context, target types.Slot) ([32]byte, error) {
if currentSlot := c.cs.CurrentSlot(); target > currentSlot {
return [32]byte{}, errors.Wrap(ErrFutureSlotRequested, fmt.Sprintf("requested=%d, current=%d", target, currentSlot))
}
for target > 0 {
slotAbove := target + 1
// don't bother searching for candidate roots when we know the target slot is genesis
for slotAbove > 1 {
if ctx.Err() != nil {
return [32]byte{}, nil, errors.Wrap(ctx.Err(), "context canceled during canonicalBlockForSlot")
return [32]byte{}, errors.Wrap(ctx.Err(), "context canceled during canonicalBlockForSlot")
}
hbs, err := c.h.HighestSlotBlocksBelow(ctx, target+1)
slot, roots, err := c.h.HighestRootsBelowSlot(ctx, slotAbove)
if err != nil {
return [32]byte{}, nil, errors.Wrap(err, fmt.Sprintf("error finding highest block w/ slot <= %d", target))
return [32]byte{}, errors.Wrap(err, fmt.Sprintf("error finding highest block w/ slot < %d", slotAbove))
}
if len(hbs) == 0 {
return [32]byte{}, nil, errors.Wrap(ErrNoBlocksBelowSlot, fmt.Sprintf("slot=%d", target))
if len(roots) == 0 {
return [32]byte{}, errors.Wrap(ErrNoBlocksBelowSlot, fmt.Sprintf("slot=%d", slotAbove))
}
r, b, err := c.bestForSlot(ctx, hbs)
r, err := c.bestForSlot(ctx, roots)
if err == nil {
// we found a valid, canonical block!
return r, b, nil
return r, nil
}
// we found a block, but it wasn't considered canonical - keep looking
if errors.Is(err, ErrNoCanonicalBlockForSlot) {
// break once we've seen slot 0 (and prevent underflow)
if hbs[0].Block().Slot() == params.BeaconConfig().GenesisSlot {
if slot == params.BeaconConfig().GenesisSlot {
break
}
target = hbs[0].Block().Slot() - 1
slotAbove = slot
continue
}
return [32]byte{}, nil, err
return [32]byte{}, err
}
b, err := c.h.GenesisBlock(ctx)
if err != nil {
return [32]byte{}, nil, errors.Wrap(err, "db error while retrieving genesis block")
}
root, _, err := c.bestForSlot(ctx, []interfaces.SignedBeaconBlock{b})
if err != nil {
return [32]byte{}, nil, errors.Wrap(err, "problem retrieving genesis block")
}
return root, b, nil
return c.h.GenesisBlockRoot(ctx)
}
// bestForSlot encapsulates several messy realities of the underlying db code, looping through multiple blocks,
// performing null/validity checks, and using CanonicalChecker to only pick canonical blocks.
func (c *CanonicalHistory) bestForSlot(ctx context.Context, hbs []interfaces.SignedBeaconBlock) ([32]byte, interfaces.SignedBeaconBlock, error) {
for _, b := range hbs {
if wrapper.BeaconBlockIsNil(b) != nil {
continue
}
root, err := b.Block().HashTreeRoot()
if err != nil {
// use this error message to wrap a sentinel error for error type matching
wrapped := errors.Wrap(ErrInvalidDBBlock, err.Error())
msg := fmt.Sprintf("could not compute hash_tree_root for block at slot=%d", b.Block().Slot())
return [32]byte{}, nil, errors.Wrap(wrapped, msg)
}
func (c *CanonicalHistory) bestForSlot(ctx context.Context, roots [][32]byte) ([32]byte, error) {
for _, root := range roots {
canon, err := c.cc.IsCanonical(ctx, root)
if err != nil {
return [32]byte{}, nil, errors.Wrap(err, "replayer could not check if block is canonical")
return [32]byte{}, errors.Wrap(err, "replayer could not check if block is canonical")
}
if canon {
return root, b, nil
return root, nil
}
}
return [32]byte{}, nil, errors.Wrap(ErrNoCanonicalBlockForSlot, "no good block for slot")
return [32]byte{}, errors.Wrap(ErrNoCanonicalBlockForSlot, "no good block for slot")
}
// ChainForSlot creates a value that satisfies the Replayer interface via db queries
@@ -122,9 +107,13 @@ func (c *CanonicalHistory) bestForSlot(ctx context.Context, hbs []interfaces.Sig
func (c *CanonicalHistory) chainForSlot(ctx context.Context, target types.Slot) (state.BeaconState, []interfaces.SignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "canonicalChainer.chainForSlot")
defer span.End()
_, b, err := c.BlockForSlot(ctx, target)
r, err := c.BlockRootForSlot(ctx, target)
if err != nil {
return nil, nil, errors.Wrap(err, fmt.Sprintf("unable to find replay data for slot=%d", target))
return nil, nil, errors.Wrapf(err, "no canonical block root found below slot=%d", target)
}
b, err := c.h.Block(ctx, r)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to retrieve canonical block for slot, root=%#x", r)
}
s, descendants, err := c.ancestorChain(ctx, b)
if err != nil {

View File

@@ -12,8 +12,6 @@ import (
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
@@ -21,7 +19,7 @@ func TestBlockForSlotFuture(t *testing.T) {
ch := &CanonicalHistory{
cs: &mockCurrentSlotter{Slot: 0},
}
_, _, err := ch.BlockForSlot(context.Background(), 1)
_, err := ch.BlockRootForSlot(context.Background(), 1)
require.ErrorIs(t, err, ErrFutureSlotRequested)
}
@@ -34,84 +32,54 @@ func TestChainForSlotFuture(t *testing.T) {
}
func TestBestForSlot(t *testing.T) {
nilBlock, err := wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlock{})
require.NoError(t, err)
nilBody, err := wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{}})
require.NoError(t, err)
derp := errors.New("fake hash tree root method no hash good")
badHTR := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{HtrErr: derp, BeaconBlockBody: &mock.BeaconBlockBody{}}}
var goodHTR [32]byte
copy(goodHTR[:], []byte{23})
var betterHTR [32]byte
copy(betterHTR[:], []byte{42})
good := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{BeaconBlockBody: &mock.BeaconBlockBody{}, Htr: goodHTR}}
better := &mock.SignedBeaconBlock{BeaconBlock: &mock.BeaconBlock{BeaconBlockBody: &mock.BeaconBlockBody{}, Htr: betterHTR}}
cases := []struct {
name string
err error
blocks []interfaces.SignedBeaconBlock
best interfaces.SignedBeaconBlock
roots [][32]byte
root [32]byte
cc CanonicalChecker
}{
{
name: "empty list",
err: ErrNoCanonicalBlockForSlot,
blocks: []interfaces.SignedBeaconBlock{},
name: "empty list",
err: ErrNoCanonicalBlockForSlot,
roots: [][32]byte{},
},
{
name: "empty SignedBeaconBlock",
err: ErrNoCanonicalBlockForSlot,
blocks: []interfaces.SignedBeaconBlock{nil},
name: "IsCanonical fail",
roots: [][32]byte{goodHTR, betterHTR},
cc: &mockCanonicalChecker{is: true, err: derp},
err: derp,
},
{
name: "empty BeaconBlock",
err: ErrNoCanonicalBlockForSlot,
blocks: []interfaces.SignedBeaconBlock{nilBlock},
name: "all non-canonical",
err: ErrNoCanonicalBlockForSlot,
roots: [][32]byte{goodHTR, betterHTR},
cc: &mockCanonicalChecker{is: false},
},
{
name: "empty BeaconBlockBody",
err: ErrNoCanonicalBlockForSlot,
blocks: []interfaces.SignedBeaconBlock{nilBody},
name: "one canonical",
cc: &mockCanonicalChecker{is: true},
root: goodHTR,
roots: [][32]byte{goodHTR},
},
{
name: "bad HTR",
err: ErrInvalidDBBlock,
blocks: []interfaces.SignedBeaconBlock{badHTR},
name: "all canonical",
cc: &mockCanonicalChecker{is: true},
root: betterHTR,
roots: [][32]byte{betterHTR, goodHTR},
},
{
name: "IsCanonical fail",
blocks: []interfaces.SignedBeaconBlock{good, better},
cc: &mockCanonicalChecker{is: true, err: derp},
err: derp,
},
{
name: "all non-canonical",
err: ErrNoCanonicalBlockForSlot,
blocks: []interfaces.SignedBeaconBlock{good, better},
cc: &mockCanonicalChecker{is: false},
},
{
name: "one canonical",
blocks: []interfaces.SignedBeaconBlock{good},
cc: &mockCanonicalChecker{is: true},
root: goodHTR,
best: good,
},
{
name: "all canonical",
blocks: []interfaces.SignedBeaconBlock{better, good},
cc: &mockCanonicalChecker{is: true},
root: betterHTR,
best: better,
},
{
name: "first wins",
blocks: []interfaces.SignedBeaconBlock{good, better},
cc: &mockCanonicalChecker{is: true},
root: goodHTR,
best: good,
name: "first wins",
cc: &mockCanonicalChecker{is: true},
root: goodHTR,
roots: [][32]byte{goodHTR, betterHTR},
},
}
for _, c := range cases {
@@ -121,10 +89,9 @@ func TestBestForSlot(t *testing.T) {
chk = c.cc
}
ch := &CanonicalHistory{cc: chk}
r, b, err := ch.bestForSlot(context.Background(), c.blocks)
r, err := ch.bestForSlot(context.Background(), c.roots)
if c.err == nil {
require.NoError(t, err)
require.DeepEqual(t, c.best, b)
require.Equal(t, c.root, r)
} else {
require.ErrorIs(t, err, c.err)
@@ -164,13 +131,11 @@ func TestCanonicalBlockForSlotHappy(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
bs, err := hist.HighestSlotBlocksBelow(ctx, c.slot+1)
_, rs, err := hist.HighestRootsBelowSlot(ctx, c.slot+1)
require.NoError(t, err)
require.Equal(t, len(bs), 1)
r, err := bs[0].Block().HashTreeRoot()
require.NoError(t, err)
require.Equal(t, hist.slotMap[c.highest], r)
cr, _, err := ch.BlockForSlot(ctx, c.slot)
require.Equal(t, len(rs), 1)
require.Equal(t, hist.slotMap[c.highest], rs[0])
cr, err := ch.BlockRootForSlot(ctx, c.slot)
require.NoError(t, err)
require.Equal(t, hist.slotMap[c.canon], cr)
})
@@ -187,47 +152,49 @@ func TestCanonicalBlockForSlotNonHappy(t *testing.T) {
}
hist := newMockHistory(t, specs, end+1)
genesis, err := hist.GenesisBlockRoot(ctx)
require.NoError(t, err)
slotOrderObserved := make([]types.Slot, 0)
derp := errors.New("HighestSlotBlocksBelow don't work")
derp := errors.New("HighestRootsBelowSlot don't work")
// since only the end block and genesis are canonical, once the slot drops below
// end, we should always get genesis
cases := []struct {
name string
slot types.Slot
canon CanonicalChecker
overrideHighest func(context.Context, types.Slot) ([]interfaces.SignedBeaconBlock, error)
overrideHighest func(context.Context, types.Slot) (types.Slot, [][32]byte, error)
slotOrderExpected []types.Slot
err error
root [32]byte
}{
{
name: "HighestSlotBlocksBelow not called for genesis",
overrideHighest: func(_ context.Context, _ types.Slot) ([]interfaces.SignedBeaconBlock, error) {
return nil, derp
name: "HighestRootsBelowSlot not called for genesis",
overrideHighest: func(_ context.Context, _ types.Slot) (types.Slot, [][32]byte, error) {
return 0, [][32]byte{}, derp
},
root: hist.slotMap[0],
},
{
name: "wrapped error from HighestSlotBlocksBelow returned",
name: "wrapped error from HighestRootsBelowSlot returned",
err: derp,
overrideHighest: func(_ context.Context, _ types.Slot) ([]interfaces.SignedBeaconBlock, error) {
return nil, derp
overrideHighest: func(_ context.Context, _ types.Slot) (types.Slot, [][32]byte, error) {
return 0, [][32]byte{}, derp
},
slot: end,
},
{
name: "HighestSlotBlocksBelow empty list",
name: "HighestRootsBelowSlot empty list",
err: ErrNoBlocksBelowSlot,
overrideHighest: func(_ context.Context, _ types.Slot) ([]interfaces.SignedBeaconBlock, error) {
return []interfaces.SignedBeaconBlock{}, nil
overrideHighest: func(_ context.Context, _ types.Slot) (types.Slot, [][32]byte, error) {
return 0, [][32]byte{}, nil
},
slot: end,
},
{
name: "HighestSlotBlocksBelow no canonical",
err: ErrNoCanonicalBlockForSlot,
name: "HighestRootsBelowSlot no canonical",
canon: &mockCanonicalChecker{is: false},
slot: end,
root: genesis,
},
{
name: "slot ordering correct - only genesis canonical",
@@ -237,11 +204,11 @@ func TestCanonicalBlockForSlotNonHappy(t *testing.T) {
}
return false, nil
}},
overrideHighest: func(_ context.Context, s types.Slot) ([]interfaces.SignedBeaconBlock, error) {
overrideHighest: func(_ context.Context, s types.Slot) (types.Slot, [][32]byte, error) {
slotOrderObserved = append(slotOrderObserved, s)
// this allows the mock HighestSlotBlocksBelow to continue to execute now that we've recorded
// this allows the mock HighestRootsBelowSlot to continue to execute now that we've recorded
// the slot in our channel
return nil, errFallThroughOverride
return 0, nil, errFallThroughOverride
},
slotOrderExpected: []types.Slot{156, 155, 150, 100},
slot: end,
@@ -255,11 +222,11 @@ func TestCanonicalBlockForSlotNonHappy(t *testing.T) {
}
return false, nil
}},
overrideHighest: func(_ context.Context, s types.Slot) ([]interfaces.SignedBeaconBlock, error) {
overrideHighest: func(_ context.Context, s types.Slot) (types.Slot, [][32]byte, error) {
slotOrderObserved = append(slotOrderObserved, s)
// this allows the mock HighestSlotBlocksBelow to continue to execute now that we've recorded
// this allows the mock HighestRootsBelowSlot to continue to execute now that we've recorded
// the slot in our channel
return nil, errFallThroughOverride
return 0, nil, errFallThroughOverride
},
slotOrderExpected: []types.Slot{156, 155, 150},
slot: end,
@@ -274,14 +241,14 @@ func TestCanonicalBlockForSlotNonHappy(t *testing.T) {
}
ch := &CanonicalHistory{h: hist, cc: canon, cs: hist}
hist.overrideHighestSlotBlocksBelow = c.overrideHighest
r, _, err := ch.BlockForSlot(ctx, c.slot)
r, err := ch.BlockRootForSlot(ctx, c.slot)
if c.err == nil {
require.NoError(t, err)
} else {
require.ErrorIs(t, err, c.err)
}
if len(c.slotOrderExpected) > 0 {
require.Equal(t, len(c.slotOrderExpected), len(slotOrderObserved), "HighestSlotBlocksBelow not called the expected number of times")
require.Equal(t, len(c.slotOrderExpected), len(slotOrderObserved), "HighestRootsBelowSlot not called the expected number of times")
for i := range c.slotOrderExpected {
require.Equal(t, c.slotOrderExpected[i], slotOrderObserved[i])
}

View File

@@ -55,24 +55,20 @@ func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
aRoot = cached.root
aState = cached.state
} else {
blks, err := s.beaconDB.HighestSlotBlocksBelow(ctx, slot)
_, roots, err := s.beaconDB.HighestRootsBelowSlot(ctx, slot)
if err != nil {
return err
}
// Given the block has been finalized, the db should not have more than one block in a given slot.
// We should error out when this happens.
if len(blks) != 1 {
if len(roots) != 1 {
return errUnknownBlock
}
missingRoot, err := blks[0].Block().HashTreeRoot()
if err != nil {
return err
}
aRoot = missingRoot
aRoot = roots[0]
// There's no need to generate the state if the state already exists in the DB.
// We can skip saving the state.
if !s.beaconDB.HasState(ctx, aRoot) {
aState, err = s.StateByRoot(ctx, missingRoot)
aState, err = s.StateByRoot(ctx, aRoot)
if err != nil {
return err
}

View File

@@ -81,7 +81,7 @@ type mockHistory struct {
states map[[32]byte]state.BeaconState
hiddenStates map[[32]byte]state.BeaconState
current types.Slot
overrideHighestSlotBlocksBelow func(context.Context, types.Slot) ([]interfaces.SignedBeaconBlock, error)
overrideHighestSlotBlocksBelow func(context.Context, types.Slot) (types.Slot, [][32]byte, error)
}
type slotList []types.Slot
@@ -98,13 +98,13 @@ func (m slotList) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
var errFallThroughOverride = errors.New("override yielding control back to real HighestSlotBlocksBelow")
var errFallThroughOverride = errors.New("override yielding control back to real HighestRootsBelowSlot")
func (m *mockHistory) HighestSlotBlocksBelow(_ context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error) {
func (m *mockHistory) HighestRootsBelowSlot(_ context.Context, slot types.Slot) (types.Slot, [][32]byte, error) {
if m.overrideHighestSlotBlocksBelow != nil {
s, err := m.overrideHighestSlotBlocksBelow(context.Background(), slot)
s, r, err := m.overrideHighestSlotBlocksBelow(context.Background(), slot)
if !errors.Is(err, errFallThroughOverride) {
return s, err
return s, r, err
}
}
if len(m.slotIndex) == 0 && len(m.slotMap) > 0 {
@@ -115,20 +115,20 @@ func (m *mockHistory) HighestSlotBlocksBelow(_ context.Context, slot types.Slot)
}
for _, s := range m.slotIndex {
if s < slot {
return []interfaces.SignedBeaconBlock{m.blocks[m.slotMap[s]]}, nil
return s, [][32]byte{m.slotMap[s]}, nil
}
}
return []interfaces.SignedBeaconBlock{}, nil
return 0, [][32]byte{}, nil
}
var errGenesisBlockNotFound = errors.New("canonical genesis block not found in db")
func (m *mockHistory) GenesisBlock(_ context.Context) (interfaces.SignedBeaconBlock, error) {
func (m *mockHistory) GenesisBlockRoot(_ context.Context) ([32]byte, error) {
genesisRoot, ok := m.slotMap[0]
if !ok {
return nil, errGenesisBlockNotFound
return [32]byte{}, errGenesisBlockNotFound
}
return m.blocks[genesisRoot], nil
return genesisRoot, nil
}
func (m *mockHistory) Block(_ context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error) {

View File

@@ -27,8 +27,8 @@ const (
// HistoryAccessor describes the minimum set of database methods needed to support the ReplayerBuilder.
type HistoryAccessor interface {
HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]interfaces.SignedBeaconBlock, error)
GenesisBlock(ctx context.Context) (interfaces.SignedBeaconBlock, error)
HighestRootsBelowSlot(ctx context.Context, slot types.Slot) (types.Slot, [][32]byte, error)
GenesisBlockRoot(ctx context.Context) ([32]byte, error)
Block(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error)
StateOrError(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
}

View File

@@ -64,13 +64,7 @@ func ValidatorFieldRoots(hasher ssz.HashFn, validator *ethpb.Validator) ([][32]b
// a list of uint64 and mixed with registry limit.
func Uint64ListRootWithRegistryLimit(balances []uint64) ([32]byte, error) {
hasher := hash.CustomSHA256Hasher()
balancesMarshaling := make([][]byte, 0, len(balances))
for i := 0; i < len(balances); i++ {
balanceBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(balanceBuf, balances[i])
balancesMarshaling = append(balancesMarshaling, balanceBuf)
}
balancesChunks, err := ssz.PackByChunk(balancesMarshaling)
balancesChunks, err := PackUint64IntoChunks(balances)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not pack balances into chunks")
}
@@ -87,3 +81,37 @@ func Uint64ListRootWithRegistryLimit(balances []uint64) ([32]byte, error) {
binary.LittleEndian.PutUint64(balancesLengthRoot, uint64(len(balances)))
return ssz.MixInLength(balancesRootsRoot, balancesLengthRoot), nil
}
// PackUint64IntoChunks packs a list of uint64 values into 32 byte roots.
func PackUint64IntoChunks(vals []uint64) ([][32]byte, error) {
// Initialize how many uint64 values we can pack
// into a single chunk(32 bytes). Each uint64 value
// would take up 8 bytes.
numOfElems := 4
sizeOfElem := 32 / numOfElems
// Determine total number of chunks to be
// allocated to provided list of unsigned
// 64-bit integers.
numOfChunks := len(vals) / numOfElems
// Add an extra chunk if the list size
// is not a perfect multiple of the number
// of elements.
if len(vals)%numOfElems != 0 {
numOfChunks++
}
chunkList := make([][32]byte, numOfChunks)
for idx, b := range vals {
// In order to determine how to pack in the uint64 value by index into
// our chunk list we need to determine a few things.
// 1) The chunk which the particular uint64 value corresponds to.
// 2) The position of the value in the chunk itself.
//
// Once we have determined these 2 values we can simply find the correct
// section of contiguous bytes to insert the value in the chunk.
chunkIdx := idx / numOfElems
idxInChunk := idx % numOfElems
chunkPos := idxInChunk * sizeOfElem
binary.LittleEndian.PutUint64(chunkList[chunkIdx][chunkPos:chunkPos+sizeOfElem], b)
}
return chunkList, nil
}

View File

@@ -9,3 +9,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().InactivityPenaltyQuotient, nil
}
func (b *BeaconState) MinSlashingPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().MinSlashingPenaltyQuotient, nil
}

View File

@@ -77,6 +77,8 @@ func InitializeFromProtoUnsafe(st *ethpb.BeaconState) (state.BeaconState, error)
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -174,24 +176,7 @@ func (b *BeaconState) Copy() state.BeaconState {
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
runtime.SetFinalizer(dst, finalizerCleanup)
return dst
}
@@ -439,3 +424,22 @@ func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{
b.dirtyIndices[index] = []uint64{}
return nil
}
func finalizerCleanup(b *BeaconState) {
fieldCount := params.BeaconConfig().BeaconStateFieldCount
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
}

View File

@@ -9,3 +9,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().InactivityPenaltyQuotientAltair, nil
}
func (b *BeaconState) MinSlashingPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().MinSlashingPenaltyQuotientAltair, nil
}

View File

@@ -78,6 +78,8 @@ func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (state.BeaconState,
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -179,23 +181,7 @@ func (b *BeaconState) Copy() state.BeaconState {
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
runtime.SetFinalizer(dst, finalizerCleanup)
return dst
}
@@ -426,3 +412,22 @@ func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{
b.dirtyIndices[index] = []uint64{}
return nil
}
func finalizerCleanup(b *BeaconState) {
fieldCount := params.BeaconConfig().BeaconStateAltairFieldCount
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
}

View File

@@ -9,3 +9,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix, nil
}
func (b *BeaconState) MinSlashingPenaltyQuotient() (uint64, error) {
return params.BeaconConfig().MinSlashingPenaltyQuotientBellatrix, nil
}

View File

@@ -78,6 +78,8 @@ func InitializeFromProtoUnsafe(st *ethpb.BeaconStateBellatrix) (state.BeaconStat
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[latestExecutionPayloadHeader] = stateutil.NewRef(1) // New in Bellatrix.
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
@@ -179,23 +181,7 @@ func (b *BeaconState) Copy() state.BeaconState {
}
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
runtime.SetFinalizer(dst, finalizerCleanup)
return dst
}
@@ -421,3 +407,22 @@ func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{
b.dirtyIndices[index] = []uint64{}
return nil
}
func finalizerCleanup(b *BeaconState) {
fieldCount := params.BeaconConfig().BeaconStateBellatrixFieldCount
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
}

View File

@@ -18,7 +18,6 @@ go_library(
],
deps = [
"//cmd:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",

View File

@@ -5,7 +5,6 @@ package flags
import (
"strings"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/urfave/cli/v2"
)
@@ -215,7 +214,7 @@ var (
SuggestedFeeRecipient = &cli.StringFlag{
Name: "suggested-fee-recipient",
Usage: "Post bellatrix, this address will receive the transaction fees produced by any blocks from this node. Default to junk whilst bellatrix is in development state. Validator client can override this value through the preparebeaconproposer api.",
Value: fieldparams.EthBurnAddressHex,
Value: params.BeaconConfig().EthBurnAddressHex,
}
// TerminalTotalDifficultyOverride specifies the total difficulty to manual overrides the `TERMINAL_TOTAL_DIFFICULTY` parameter.
TerminalTotalDifficultyOverride = &cli.StringFlag{

View File

@@ -0,0 +1,29 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["jwt.go"],
importpath = "github.com/prysmaticlabs/prysm/cmd/beacon-chain/jwt",
visibility = ["//visibility:public"],
deps = [
"//cmd:go_default_library",
"//crypto/rand:go_default_library",
"//io/file:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["jwt_test.go"],
embed = [":go_default_library"],
deps = [
"//cmd:go_default_library",
"//io/file:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)

View File

@@ -0,0 +1,71 @@
package jwt
import (
"errors"
"path/filepath"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/crypto/rand"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
const (
secretFileName = "jwt.hex"
)
var Commands = &cli.Command{
Name: "generate-auth-secret",
Usage: "creates a random, 32 byte hex string in a plaintext file to be used for authenticating JSON-RPC requests. If no --output-file flag is defined, the file will be created in the current working directory",
Description: `creates a random, 32 byte hex string in a plaintext file to be used for authenticating JSON-RPC requests. If no --output-file flag is defined, the file will be created in the current working directory`,
Flags: cmd.WrapFlags([]cli.Flag{
cmd.JwtOutputFileFlag,
}),
Action: generateAuthSecretInFile,
}
func generateAuthSecretInFile(c *cli.Context) error {
fileName := secretFileName
specifiedFilePath := c.String(cmd.JwtOutputFileFlag.Name)
if len(specifiedFilePath) > 0 {
fileName = specifiedFilePath
}
var err error
fileName, err = file.ExpandPath(fileName)
if err != nil {
return err
}
fileDir := filepath.Dir(fileName)
exists, err := file.HasDir(fileDir)
if err != nil {
return err
}
if !exists {
if err := file.MkdirAll(fileDir); err != nil {
return err
}
}
secret, err := generateRandomHexString()
if err != nil {
return err
}
if err := file.WriteFile(fileName, []byte(secret)); err != nil {
return err
}
logrus.Infof("Successfully wrote JSON-RPC authentication secret to file %s", fileName)
return nil
}
func generateRandomHexString() (string, error) {
secret := make([]byte, 32)
randGen := rand.NewGenerator()
n, err := randGen.Read(secret)
if err != nil {
return "", err
} else if n <= 0 {
return "", errors.New("rand: unexpected length")
}
return hexutil.Encode(secret), nil
}

View File

@@ -0,0 +1,86 @@
package jwt
import (
"flag"
"os"
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/urfave/cli/v2"
)
func Test_generateJWTSecret(t *testing.T) {
t.Run("command should be available", func(t *testing.T) {
generateJwtCommand := Commands
require.Equal(t, true, generateJwtCommand.Name == "generate-auth-secret")
})
t.Run("should create proper file in current directory", func(t *testing.T) {
require.NoError(t, os.RemoveAll(secretFileName))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(secretFileName))
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, secretFileName)
})
t.Run("should create proper file in specified folder", func(t *testing.T) {
customOutput := filepath.Join("data", "item.txt")
require.NoError(t, os.RemoveAll(filepath.Dir(customOutput)))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(filepath.Dir(customOutput)))
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(cmd.JwtOutputFileFlag.Name, customOutput, "")
require.NoError(t, set.Set(cmd.JwtOutputFileFlag.Name, customOutput))
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, customOutput)
})
t.Run("creates proper file in nested specified folder", func(t *testing.T) {
rootDirectory := "data"
customOutputPath := filepath.Join(rootDirectory, "nest", "nested", "item.txt")
require.NoError(t, os.RemoveAll(filepath.Dir(customOutputPath)))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(rootDirectory))
_, err := os.Stat(customOutputPath)
require.Equal(t, true, err != nil)
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(cmd.JwtOutputFileFlag.Name, customOutputPath, "")
require.NoError(t, set.Set(cmd.JwtOutputFileFlag.Name, customOutputPath))
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, customOutputPath)
})
}
func checkAuthFileIntegrity(t testing.TB, fPath string) {
fileInfo, err := os.Stat(fPath)
require.NoError(t, err)
require.Equal(t, true, fileInfo != nil)
enc, err := file.ReadFileAsBytes(fPath)
require.NoError(t, err)
decoded, err := hexutil.Decode(string(enc))
require.NoError(t, err)
require.Equal(t, 32, len(decoded))
}

View File

@@ -1,4 +1,4 @@
// Package cmd defines the command line flags for the shared utlities.
// Package cmd defines the command line flags for the shared utilities.
package cmd
import (
@@ -255,6 +255,12 @@ var (
Usage: "Specifies the timeout value for API requests in seconds",
Value: 120,
}
// JwtOutputFileFlag specifies the JWT file path that gets generated into when invoked by generate-jwt-secret.
JwtOutputFileFlag = &cli.StringFlag{
Name: "output-file",
Usage: "Target file path for outputting a generated JWT secret to be used for JSON-RPC authentication",
Aliases: []string{"o"},
}
)
// LoadFlagsFromConfig sets flags values from config file if ConfigFileFlag is set.

View File

@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"accounts.go",
"backup.go",
"delete.go",
"list.go",
"wallet_utils.go",
@@ -14,6 +15,7 @@ go_library(
"//cmd:go_default_library",
"//cmd/validator/flags:go_default_library",
"//config/features:go_default_library",
"//io/prompt:go_default_library",
"//runtime/tos:go_default_library",
"//validator/accounts:go_default_library",
"//validator/accounts/iface:go_default_library",
@@ -29,20 +31,27 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["delete_test.go"],
srcs = [
"backup_test.go",
"delete_test.go",
],
embed = [":go_default_library"],
deps = [
"//cmd/validator/flags:go_default_library",
"//config/params:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//io/file:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//time:go_default_library",
"//validator/accounts:go_default_library",
"//validator/accounts/iface:go_default_library",
"//validator/accounts/wallet:go_default_library",
"//validator/keymanager:go_default_library",
"//validator/keymanager/derived:go_default_library",
"//validator/keymanager/local:go_default_library",
"//validator/testing:go_default_library",
"@com_github_google_uuid//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",

View File

@@ -111,13 +111,16 @@ var Commands = &cli.Command{
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
return err
}
return tos.VerifyTosAcceptedOrPrompt(cliCtx)
},
Action: func(cliCtx *cli.Context) error {
if err := tos.VerifyTosAcceptedOrPrompt(cliCtx); err != nil {
return err
}
if err := features.ConfigureValidator(cliCtx); err != nil {
return err
}
if err := accounts.BackupAccountsCli(cliCtx); err != nil {
return nil
},
Action: func(cliCtx *cli.Context) error {
if err := accountsBackup(cliCtx); err != nil {
log.Fatalf("Could not backup accounts: %v", err)
}
return nil

View File

@@ -0,0 +1,82 @@
package accounts
import (
"strings"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/io/prompt"
"github.com/prysmaticlabs/prysm/validator/accounts"
"github.com/prysmaticlabs/prysm/validator/accounts/userprompt"
"github.com/prysmaticlabs/prysm/validator/client"
"github.com/urfave/cli/v2"
)
const backupPromptText = "Enter the directory where your backup.zip file will be written to"
func accountsBackup(c *cli.Context) error {
w, km, err := walletWithKeymanager(c)
if err != nil {
return err
}
dialOpts := client.ConstructDialOptions(
c.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name),
c.String(flags.CertFlag.Name),
c.Uint(flags.GrpcRetriesFlag.Name),
c.Duration(flags.GrpcRetryDelayFlag.Name),
)
grpcHeaders := strings.Split(c.String(flags.GrpcHeadersFlag.Name), ",")
opts := []accounts.Option{
accounts.WithWallet(w),
accounts.WithKeymanager(km),
accounts.WithGRPCDialOpts(dialOpts),
accounts.WithBeaconRPCProvider(c.String(flags.BeaconRPCProviderFlag.Name)),
accounts.WithGRPCHeaders(grpcHeaders),
}
// Get full set of public keys from the keymanager.
publicKeys, err := km.FetchValidatingPublicKeys(c.Context)
if err != nil {
return errors.Wrap(err, "could not fetch validating public keys")
}
// Filter keys either from CLI flag or from interactive session.
filteredPubKeys, err := accounts.FilterPublicKeysFromUserInput(
c,
flags.BackupPublicKeysFlag,
publicKeys,
userprompt.SelectAccountsBackupPromptText,
)
if err != nil {
return errors.Wrap(err, "could not filter public keys for backup")
}
opts = append(opts, accounts.WithFilteredPubKeys(filteredPubKeys))
// Input the directory where they wish to backup their accounts.
backupsDir, err := userprompt.InputDirectory(c, backupPromptText, flags.BackupDirFlag)
if err != nil {
return errors.Wrap(err, "could not parse keys directory")
}
// Ask the user for their desired password for their backed up accounts.
backupsPassword, err := prompt.InputPassword(
c,
flags.BackupPasswordFile,
"Enter a new password for your backed up accounts",
"Confirm new password",
true,
prompt.ValidatePasswordInput,
)
if err != nil {
return errors.Wrap(err, "could not determine password for backed up accounts")
}
opts = append(opts, accounts.WithBackupsDir(backupsDir))
opts = append(opts, accounts.WithBackupsPassword(backupsPassword))
acc, err := accounts.NewCLIManager(opts...)
if err != nil {
return err
}
return acc.Backup(c.Context)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/io/file"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/validator/accounts"
"github.com/prysmaticlabs/prysm/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/validator/keymanager"
@@ -52,7 +53,7 @@ func TestBackupAccounts_Noninteractive_Derived(t *testing.T) {
backupPasswordFile: backupPasswordFile,
backupDir: backupDir,
})
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Derived,
@@ -93,10 +94,10 @@ func TestBackupAccounts_Noninteractive_Derived(t *testing.T) {
})
// Next, we attempt to backup the accounts.
require.NoError(t, BackupAccountsCli(cliCtx))
require.NoError(t, accountsBackup(cliCtx))
// We check a backup.zip file was created at the output path.
zipFilePath := filepath.Join(backupDir, archiveFilename)
zipFilePath := filepath.Join(backupDir, accounts.ArchiveFilename)
assert.DeepEqual(t, true, file.FileExists(zipFilePath))
// We attempt to unzip the file and verify the keystores do match our accounts.
@@ -169,7 +170,7 @@ func TestBackupAccounts_Noninteractive_Imported(t *testing.T) {
backupPasswordFile: backupPasswordFile,
backupDir: backupDir,
})
_, err = CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
_, err = accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
@@ -180,13 +181,13 @@ func TestBackupAccounts_Noninteractive_Imported(t *testing.T) {
// We attempt to import accounts we wrote to the keys directory
// into our newly created wallet.
require.NoError(t, ImportAccountsCli(cliCtx))
require.NoError(t, accounts.ImportAccountsCli(cliCtx))
// Next, we attempt to backup the accounts.
require.NoError(t, BackupAccountsCli(cliCtx))
require.NoError(t, accountsBackup(cliCtx))
// We check a backup.zip file was created at the output path.
zipFilePath := filepath.Join(backupDir, archiveFilename)
zipFilePath := filepath.Join(backupDir, accounts.ArchiveFilename)
assert.DeepEqual(t, true, file.FileExists(zipFilePath))
// We attempt to unzip the file and verify the keystores do match our accounts.

View File

@@ -13,7 +13,7 @@ go_library(
"//validator:__subpackages__",
],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//io/file:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],

View File

@@ -7,7 +7,7 @@ import (
"runtime"
"time"
field_params "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/urfave/cli/v2"
)
@@ -327,21 +327,36 @@ var (
// FeeRecipientConfigFileFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigFileFlag = &cli.StringFlag{
Name: "fee-recipient-config-file",
Usage: "Set path to a JSON file containing validator mappings to ETH addresses for receiving transaction fees when proposing blocks (i.e. --fee-recipient-config-file=/path/to/proposer.json). File format found in docs",
Usage: "DEPRECATED, please use proposer-settings-file",
Value: "",
}
// FeeRecipientConfigURLFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigURLFlag = &cli.StringFlag{
Name: "fee-recipient-config-url",
Usage: "Set URL to a REST endpoint containing validator mappings to ETH addresses for receiving transaction fees when proposing blocks (i.e. --fee-recipient-config-url=https://example.com/api/getConfig). File format found in docs",
Usage: "DEPRECATED, please use proposer-settings-url",
Value: "",
}
// ProposerSettingsFlag defines the path or URL to a file with proposer config.
ProposerSettingsFlag = &cli.StringFlag{
Name: "proposer-settings-file",
Usage: "Set path to a YAML or JSON file containing validator settings used when proposing blocks such as (fee recipient and gas limit) (i.e. --proposer-settings-file=/path/to/proposer.json). File format found in docs",
Value: "",
}
// ProposerSettingsURLFlag defines the path or URL to a file with proposer config.
ProposerSettingsURLFlag = &cli.StringFlag{
Name: "proposer-settings-url",
Usage: "Set URL to a REST endpoint containing validator settings used when proposing blocks such as (fee recipient) (i.e. --proposer-settings-url=https://example.com/api/getConfig). File format found in docs",
Value: "",
}
// SuggestedFeeRecipientFlag defines the address of the fee recipient.
SuggestedFeeRecipientFlag = &cli.StringFlag{
Name: "suggested-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block. Overrides the --fee-recipient-config-file flag if set",
Value: field_params.EthBurnAddressHex,
Name: "suggested-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block." +
" note that this is only a suggestion when integrating with a Builder API, which may choose to specify a different fee recipient as payment for the blocks it builds." +
" For additional setting overrides use the --" + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " Flags. ",
Value: params.BeaconConfig().EthBurnAddressHex,
}
)

View File

@@ -78,6 +78,8 @@ var appFlags = []cli.Flag{
flags.FeeRecipientConfigFileFlag,
flags.FeeRecipientConfigURLFlag,
flags.SuggestedFeeRecipientFlag,
flags.ProposerSettingsURLFlag,
flags.ProposerSettingsFlag,
////////////////////
cmd.DisableMonitoringFlag,
cmd.MonitoringHostFlag,

Some files were not shown because too many files have changed in this diff Show More