Compare commits

...

43 Commits

Author SHA1 Message Date
terence tsao
ac6ecbac0b Revert "Use --http-web3provider for Execution Engine Connection (#10307)"
This reverts commit 3579551f15.
2022-03-25 08:52:33 -07:00
Potuz
a5dc40393d Allow inner protoarray nodes to become VALID (#10418)
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-25 15:35:44 +00:00
Raul Jordan
b238235210 prevent run (#10432) 2022-03-25 11:08:38 -04:00
terence tsao
73a6a85069 Ensure finalized root can't be zeros (#10422) 2022-03-24 23:55:54 +00:00
terence tsao
ac4a30588f Properly initialize replayer builder for rpc (#10426) 2022-03-24 23:12:47 +00:00
Raul Jordan
3579551f15 Use --http-web3provider for Execution Engine Connection (#10307)
* combine endpoints

* initialization in the start function instead

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2022-03-24 20:53:07 +00:00
Nishant Das
956e7a7563 Update Go-Ethereum (#10417)
* add changes

* fix CI

* wait here

* Fix gazelle with the correct directive for libp2p

Co-authored-by: prestonvanloon <preston@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-24 19:00:53 +00:00
kasey
acd7e62cfb fix issue #10420 (#10421)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-03-24 18:32:39 +00:00
Radosław Kapka
165f0667f0 Implement getDebugChainHeadsV2 API endpoint (#10419)
* protos

* grpc

* middleware

* update comments
2022-03-24 13:08:40 +00:00
kasey
49826ebe28 Checkpoint Sync 2/5 - API support for retrieving weak subjectivity data (#10384)
also refactor replayer code for better reuse:
- separately expose stategen's canonical block func
- CanonicalHistory in ws api, NewCanonicalBuilder
- refactor CanonicalHistory into a ReplayerBuilder

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-03-23 22:54:07 +00:00
terence tsao
3ad6abd9c0 Add tracings for engine api methods (#10407)
* Add engine call tracings

* Update BUILD.bazel

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-23 22:17:09 +00:00
terence tsao
c61f54176d Ignore attestations voting for the wrong finalized checkpoint (#10295)
* Ignore attestations voting for the wrong finalized checkpoint

* aggregate

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-23 21:48:26 +00:00
Potuz
0165351db3 Setup Optimistic sync when starting the service (#10397)
* Load Last Validated Checkpoint at service startup and initialize
Forkchoice

* fix spectests

* Update service_test.go

* wip

* Fix blockchain tests

* remove unnecessary summary saves

* Terence's suggestion

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-23 21:11:36 +00:00
Potuz
8f49167117 return invalid roots from SetOptimisticToInvalid (#10403)
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-23 15:55:05 -03:00
Nishant Das
b4c27f64de Add Method To Modify Participation Bits (#10409)
* fix up all tests

* Update beacon-chain/state/v2/BUILD.bazel

* add tests
2022-03-23 23:18:02 +08:00
Preston Van Loon
e4ac5e74b7 RPC: Deduplicate block production logic (#10368)
* RPC: Deduplicate block production logic

* Surface error to caller

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2022-03-23 12:51:20 +00:00
Nishant Das
0b9b635646 Block Processing And State Transition Improvments (#10408)
* cleanup

* clean up more

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2022-03-23 11:57:48 +00:00
Leo Lara
c8c1d04c07 Complete coverage for in beacon chain state for getters checkpoint (#10373)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2022-03-23 09:58:45 +00:00
james-prysm
601987faf2 Fee Recipient : ux log fixes and prevent validator from calling beacon node if flags are not provided. (#10406)
* initial commit

* improving logs
2022-03-22 13:13:59 -05:00
james-prysm
a060d765b3 rename prepare beacon proposer to fee recipient (#10404) 2022-03-22 15:00:50 +00:00
Potuz
5550334956 set optimistic status on notify payload (#10398)
* set optimistic status on notify payload

* pre-execution blocks are never optimistic

* add unit test

* add test for lengths

* Update optimistic_sync_test.go

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-22 03:35:02 +00:00
Mike Neuder
002253bba3 Migrating Keymanager account list functionality into each type (#10382)
* Migrating Keymanager account list functionality into each keymanager type

* Addressing review comments

* Adding newline at end of BUILD.bazel

* bazel run //:gazelle -- fix

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2022-03-22 03:04:09 +00:00
Radosław Kapka
c055642f79 Return correct header root from API calls (#10401)
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-22 01:56:44 +00:00
terence tsao
c7d64c03ac Insert payload hash to forkchoice store nodes (#10400)
* Insert payload hash to forkchoice store nodes

* Fix tests

* Update new_slot_test.go

* Update beacon-chain/blockchain/process_block.go

Co-authored-by: Potuz <potuz@prysmaticlabs.com>

Co-authored-by: Potuz <potuz@prysmaticlabs.com>
2022-03-22 01:20:42 +00:00
james-prysm
131fb43ba7 Fee recipient flag rename for beacon node (#10402)
* initial commit

* fixing comment

* fixing bazel
2022-03-21 21:25:33 +00:00
kasey
cf0bd633f0 Checkpoint Sync 1/5 - fork/version detection and unmarshaling support (#10380)
* fork/version detection and unmarshaling support

* Update config/params/config.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update proto/detect/configfork.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* PR feedback

* move ssz initialization into the detect package

* clarify comment

* VersionForEpoch is much simpler/clearer in reverse

* simpler VersionForEpoch; build AllConfigs in init

* use fieldparams for Version

* Update proto/detect/configfork_test.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* remove custom ForkName type, use runtime/version

* pr cleanup

* random fix from bad gh ui suggestion; privatize

* privatize fieldSpec methods; + unit tests

* Update proto/detect/configfork.go

Co-authored-by: Potuz <potuz@prysmaticlabs.com>

* fix bad github ui suggestion

* ensure unique versions for simpler config match

* fmt & adding unit test for ByState()

* table-driven unit test for ByState

* TestUnmarshalState

* OrderedSchedule -> network/forks per PR feedback

* goimports

* lint fixes

* move proto/detect -> ssz/encoding/detect

* use typeUndefined in String

* backport config tests from e2e PR

* fix config parity test; make debugging it easier

* lint

* fix fork schedule initialization

* cleanup

* fix build

* fix big ole derp

* anything for you, deep source

* goimportsss

* InitializeForkSchedule in LoadChainConfigFile

* PR feedback

Co-authored-by: kasey <kasey@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Potuz <potuz@prysmaticlabs.com>
2022-03-21 19:43:41 +00:00
james-prysm
df8da80db8 Implement Fee Recipient: CLI flags and Calling Beacon API (#10312)
* send proposer data to config

* wip implementation with file and url based config import

* improving logic on get validator index

* fix function

* optimizing function for map and address bug

* fixing log

* update cache if it doesn't exist

* updating flags

* initial unit test scaffold

* fixing validator to call rpc call, removed temporary dependency

* adding the API calls for the runner

* fixing broken build

* fixing deepsource

* fixing interface

* fixing fatal

* fixing more deepsource issues

* adding test placeholders

* updating proposer config to add validation

* changing how if statement throws error

* removing unneeded validation, validating in a different way

* wip improving tests

* more unit test work

* fixing unit test

* fixing unit tests and edge cases

* adding unit tests and adjusting how the config is created

* fixing bazel builds

* fixing proto generation

* fixing imports

* fixing unit tests

* Update cmd/validator/flags/flags.go

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

* updating flags based on comments, fixing unit tests

* fixing bazel

* removing unneeded function

* fixing unit tests

* refactors and unit test fixes based on comments

* fixing bazel build

* refactor the cache out fo the fee recipient function

* adding usecase for multiple fee recipient

* refactor burn name

* Update validator/client/validator.go

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

* Update validator/client/validator.go

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

* Update validator/client/validator.go

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

* fixing bug with validator index based on code review

* edited flag descriptions to better communicate usage

* fixing manual reference to flag name

* fixing code review comments

* fixing linting

* Update validator/client/validator.go

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

* addressing comments and renaming functions

* fixing linting

* Update cmd/validator/flags/flags.go

* Update cmd/validator/flags/flags.go

* Update cmd/validator/flags/flags.go

* improving comments

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2022-03-21 13:48:02 -05:00
Raul Jordan
dc527a3c80 Remove Visibility Check Script for Bazel (#10391)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-21 17:03:52 +00:00
Potuz
94f80bd208 Call FCU on update head after process attestation (#10394)
* Call FCU on update head after process attestation

* add test

* fix test

* terence review

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-21 01:50:08 +00:00
terence tsao
c6bfefa0bc Add notifyNewPayload to onBlockBatch (#10392) 2022-03-18 09:36:46 -07:00
Radosław Kapka
3446eaa5f2 Add missing tests to state retrieval (#10390) 2022-03-17 18:40:07 +00:00
terence tsao
193866c731 Add fcu to batch blk processing (#10371) 2022-03-17 07:35:50 -07:00
terence tsao
b90ce1b60f Bellatrix fork choice spec tests (#10381)
* Add bellatrix forkchoice spec tests

* Update service.go

* better conversion
2022-03-17 05:30:44 +00:00
terence tsao
8449d24ed0 Fix BaseFeePerGas endianess (#10376)
* Fix base fee endianess and add more test entropys

* Update json_marshal_unmarshal_test.go

* Update setters_test.go

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-16 22:12:08 +00:00
Radosław Kapka
b92b01f379 API middleware support for bellatrix in produceBlockV2 (#10374) 2022-03-16 21:44:15 +00:00
terence tsao
bcd180ee4d Remove deprecated set_optimistic_to_valid calls (#10370) 2022-03-16 21:14:13 +00:00
Raul Jordan
5f10e51a49 Add API Middleware Support for Fee Recipients Endpoint (#10377)
* add in success log

* add in info log

* uint64 for schema

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-16 20:02:26 +00:00
terence tsao
c4f03868ce Update default fee warning (#10378)
* Add fcu to batch blk processing

* Log error only if is burned

* Revert some changes

* Burn address
2022-03-16 19:26:51 +00:00
james-prysm
d55c0d74dd PrepareBeaconProposer API (#10367)
* intial commit implementing prepare beacon proposal

* removing files accidently checked in by goland

* removing files accidently checked in by goland

* Delete BUILD 2

* Delete armeabi_cc_toolchain_config 2.bzl

* Delete builtin_include_directory_paths 2

* Delete cc_toolchain_config 2.bzl

* Delete BUILD 2.bazel

* Delete cc_wrapper 2.sh

* Delete module 2.modulemap

* Delete BUILD 2

* Delete BUILD 2

* Delete WORKSPACE 2

* Delete cc_toolchain_config_linux_arm64 2.bzl

* Delete cc_toolchain_config_osx 2.bzl

* Delete cc_toolchain_config_windows 2.bzl

* Delete 0258716f94e00f9df0da869fd97f9e0d0c6ac83eb528677d918f0ac9be5f4b8d 2

* Delete test-unmarshal-bad 2.json

* Delete test-unmarshal-good 2.json

* adding simple unit tests

* removing duplicate imports

* adding bazel changes

* adding validation and switched to table driven tests

* updating based on comments

* adding to test retrieve from db

* fixing bazel
2022-03-16 11:17:41 -04:00
Potuz
807b71244b Add database methods for optimistic sync (#10357)
* Add database methods for optimistic sync

* Add epoch comparison

* add extra epoch comparison

* Summary instead of block

* fix tests

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-15 20:52:59 +00:00
Potuz
2744eba391 Update optimisticCandidateBlock to specs version (#10355)
* Update optimisticCandidateBlock to specs version

* fix double import

* Change error message
2022-03-15 19:12:52 +00:00
Leo Lara
5507558678 Some improvements for the state package testing (#10316)
* Refactor to unify state getters block tests

* Add reference tests to Altair and Bellatrix versions ofthe state

* Fix function naming convetion

* Add state-native/v2/references_test.go and state-native/v3/references_test.go

* Gazelle run

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2022-03-15 18:32:09 +00:00
terence tsao
8cecd4e8bf Log bellatrix fields (#10365)
* Log bellatrix fields

* Update log.go

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-15 14:02:39 +00:00
274 changed files with 9808 additions and 4298 deletions

View File

@@ -7,7 +7,7 @@ cd $GITHUB_WORKSPACE
cp go.mod go.mod.orig
cp go.sum go.sum.orig
go mod tidy
go mod tidy -compat=1.17
echo "Checking go.mod and go.sum:"
checks=0

View File

@@ -7,6 +7,8 @@ import (
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"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"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
@@ -323,13 +325,44 @@ func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
return false, nil
}
return s.cfg.ForkChoiceStore.IsOptimistic(ctx, s.head.root)
return s.IsOptimisticForRoot(ctx, s.head.root)
}
// IsOptimisticForRoot takes the root and slot as aguments instead of the current head
// and returns true if it is optimistic.
func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool, error) {
return s.cfg.ForkChoiceStore.IsOptimistic(ctx, root)
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, root)
if err == nil {
return optimistic, nil
}
if err != protoarray.ErrUnknownNodeRoot && err != doublylinkedtree.ErrNilNode {
return false, err
}
ss, err := s.cfg.BeaconDB.StateSummary(ctx, root)
if err != nil {
return false, err
}
if ss == nil {
return false, errInvalidNilSummary
}
validatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(ctx)
if err != nil {
return false, err
}
if slots.ToEpoch(ss.Slot) > validatedCheckpoint.Epoch {
return true, nil
}
if slots.ToEpoch(ss.Slot)+1 < validatedCheckpoint.Epoch {
return false, nil
}
lastValidated, err := s.cfg.BeaconDB.StateSummary(ctx, bytesutil.ToBytes32(validatedCheckpoint.Root))
if err != nil {
return false, err
}
return ss.Slot > lastValidated.Slot, nil
}
// SetGenesisTime sets the genesis time of beacon chain.

View File

@@ -288,11 +288,11 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
ctx := context.Background()
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0,
params.BeaconConfig().ZeroHash)}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
roots, slots := c.ChainHeads()
require.DeepEqual(t, [][32]byte{{'c'}, {'d'}, {'e'}}, roots)
@@ -302,11 +302,11 @@ func TestService_ChainHeads_ProtoArray(t *testing.T) {
func TestService_ChainHeads_DoublyLinkedTree(t *testing.T) {
ctx := context.Background()
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 0, 0))
roots, slots := c.ChainHeads()
require.Equal(t, 3, len(roots))
@@ -384,8 +384,8 @@ func TestService_IsOptimistic_ProtoArray(t *testing.T) {
ctx := context.Background()
c := &Service{cfg: &config{ForkChoiceStore: protoarray.New(0, 0, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
opt, err := c.IsOptimistic(ctx)
require.NoError(t, err)
@@ -400,8 +400,8 @@ func TestService_IsOptimistic_DoublyLinkedTree(t *testing.T) {
ctx := context.Background()
c := &Service{cfg: &config{ForkChoiceStore: doublylinkedtree.New(0, 0)}, head: &head{slot: 101, root: [32]byte{'b'}}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
opt, err := c.IsOptimistic(ctx)
require.NoError(t, err)
@@ -419,8 +419,8 @@ 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, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'})
require.NoError(t, err)
@@ -430,10 +430,86 @@ 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'}}}
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, c.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
opt, err := c.IsOptimisticForRoot(ctx, [32]byte{'a'})
require.NoError(t, err)
require.Equal(t, true, opt)
}
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, [32]byte{})}, head: &head{slot: 101, root: [32]byte{'b'}}}
c.head = &head{root: params.BeaconConfig().ZeroHash}
b := util.NewBeaconBlock()
b.Block.Slot = 10
br, err := b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b)))
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: br[:], Slot: 10}))
optimisticBlock := util.NewBeaconBlock()
optimisticBlock.Block.Slot = 11
optimisticRoot, err := optimisticBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(optimisticBlock)))
validatedBlock := util.NewBeaconBlock()
validatedBlock.Block.Slot = 9
validatedRoot, err := validatedBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(validatedBlock)))
validatedCheckpoint := &ethpb.Checkpoint{Root: br[:]}
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, validatedCheckpoint))
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: optimisticRoot[:], Slot: 11}))
optimistic, err := c.IsOptimisticForRoot(ctx, optimisticRoot)
require.NoError(t, err)
require.Equal(t, true, optimistic)
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: validatedRoot[:], Slot: 9}))
validated, err := c.IsOptimisticForRoot(ctx, validatedRoot)
require.NoError(t, err)
require.Equal(t, false, validated)
}
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.head = &head{root: params.BeaconConfig().ZeroHash}
b := util.NewBeaconBlock()
b.Block.Slot = 10
br, err := b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b)))
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: br[:], Slot: 10}))
optimisticBlock := util.NewBeaconBlock()
optimisticBlock.Block.Slot = 11
optimisticRoot, err := optimisticBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(optimisticBlock)))
validatedBlock := util.NewBeaconBlock()
validatedBlock.Block.Slot = 9
validatedRoot, err := validatedBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(validatedBlock)))
validatedCheckpoint := &ethpb.Checkpoint{Root: br[:]}
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, validatedCheckpoint))
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: optimisticRoot[:], Slot: 11}))
optimistic, err := c.IsOptimisticForRoot(ctx, optimisticRoot)
require.NoError(t, err)
require.Equal(t, true, optimistic)
require.NoError(t, beaconDB.SaveStateSummary(context.Background(), &ethpb.StateSummary{Root: validatedRoot[:], Slot: 9}))
validated, err := c.IsOptimisticForRoot(ctx, validatedRoot)
require.NoError(t, err)
require.Equal(t, false, validated)
}

View File

@@ -9,4 +9,11 @@ var (
errNilBestJustifiedInStore = errors.New("nil best justified checkpoint returned from store")
// errNilFinalizedInStore is returned when a nil finalized checkpt is returned from store.
errNilFinalizedInStore = errors.New("nil finalized checkpoint returned from store")
// errInvalidNilSummary is returned when a nil summary is returned from the DB.
errInvalidNilSummary = errors.New("nil summary returned from the DB")
// errNilParentInDB is returned when a nil parent block is returned from the DB.
errNilParentInDB = errors.New("nil parent block in DB")
// errWrongBlockCound is returned when the wrong number of blocks or
// block roots is used
errWrongBlockCount = errors.New("wrong number of blocks or block roots")
)

View File

@@ -86,11 +86,6 @@ func (s *Service) updateHead(ctx context.Context, balances []uint64) error {
if err := s.insertBlockToForkChoiceStore(ctx, jb.Block(), headStartRoot, f, j); err != nil {
return err
}
// TODO(10261) send optimistic status
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headStartRoot); err != nil {
return err
}
}
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx, j.Epoch, headStartRoot, balances, f.Epoch)

View File

@@ -6,6 +6,7 @@ import (
"time"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -17,7 +18,7 @@ import (
var log = logrus.WithField("prefix", "blockchain")
// logs state transition related data every slot.
func logStateTransitionData(b block.BeaconBlock) {
func logStateTransitionData(b block.BeaconBlock) error {
log := log.WithField("slot", b.Slot())
if len(b.Body().Attestations()) > 0 {
log = log.WithField("attestations", len(b.Body().Attestations()))
@@ -34,13 +35,23 @@ func logStateTransitionData(b block.BeaconBlock) {
if len(b.Body().VoluntaryExits()) > 0 {
log = log.WithField("voluntaryExits", len(b.Body().VoluntaryExits()))
}
if b.Version() == version.Altair {
if b.Version() == version.Altair || b.Version() == version.Bellatrix {
agg, err := b.Body().SyncAggregate()
if err == nil {
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
if err != nil {
return err
}
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
}
if b.Version() == version.Bellatrix {
p, err := b.Body().ExecutionPayload()
if err != nil {
return err
}
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)))
log = log.WithField("txCount", len(p.Transactions))
}
log.Info("Finished applying state transition")
return nil
}
func logBlockSyncStatus(block block.BeaconBlock, blockRoot [32]byte, finalized *ethpb.Checkpoint, receivedTime time.Time, genesisTime uint64) error {

View File

@@ -3,6 +3,7 @@ package blockchain
import (
"testing"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
@@ -11,6 +12,17 @@ import (
)
func Test_logStateTransitionData(t *testing.T) {
payloadBlk := &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
SyncAggregate: &ethpb.SyncAggregate{},
ExecutionPayload: &enginev1.ExecutionPayload{
BlockHash: []byte{1, 2, 3},
Transactions: [][]byte{{}, {}},
},
},
}
wrappedPayloadBlk, err := wrapper.WrappedBeaconBlock(payloadBlk)
require.NoError(t, err)
tests := []struct {
name string
b block.BeaconBlock
@@ -55,11 +67,15 @@ func Test_logStateTransitionData(t *testing.T) {
VoluntaryExits: []*ethpb.SignedVoluntaryExit{{}}}}),
want: "\"Finished applying state transition\" attestations=1 attesterSlashings=1 deposits=1 prefix=blockchain proposerSlashings=1 slot=0 voluntaryExits=1",
},
{name: "has payload",
b: wrappedPayloadBlk,
want: "\"Finished applying state transition\" payloadHash=0x010203 prefix=blockchain slot=0 syncBitsCount=0 txCount=2",
},
}
for _, tt := range tests {
hook := logTest.NewGlobal()
t.Run(tt.name, func(t *testing.T) {
logStateTransitionData(tt.b)
require.NoError(t, logStateTransitionData(tt.b))
require.LogsContain(t, hook, tt.want)
})
}

View File

@@ -25,11 +25,11 @@ func TestService_newSlot(t *testing.T) {
}
ctx := context.Background()
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, 0, 0)) // genesis
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, 0, 0)) // finalized
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, 0, 0)) // justified
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, 0, 0)) // best justified
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, 0, 0)) // bad
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, [32]byte{}, 0, 0)) // genesis
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 32, [32]byte{'a'}, [32]byte{}, [32]byte{}, 0, 0)) // finalized
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 64, [32]byte{'b'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // justified
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 96, [32]byte{'c'}, [32]byte{'a'}, [32]byte{}, 0, 0)) // best justified
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 97, [32]byte{'d'}, [32]byte{}, [32]byte{}, 0, 0)) // bad
type args struct {
slot types.Slot

View File

@@ -16,12 +16,16 @@ import (
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
"github.com/prysmaticlabs/prysm/runtime/version"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
// 1. Re-organizes the execution payload chain and corresponding state to make head_block_hash the head.
// 2. Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including finalized_block_hash.
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.BeaconBlock, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.BeaconBlock, headRoot [32]byte, finalizedRoot [32]byte) (*enginev1.PayloadIDBytes, error) {
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
defer span.End()
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
return nil, errors.New("nil head block")
}
@@ -76,20 +80,24 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headBlk block.Beac
return nil, errors.Wrap(err, "could not notify forkchoice update from execution engine")
}
}
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, s.headRoot()); err != nil {
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headRoot); err != nil {
return nil, errors.Wrap(err, "could not set block to valid")
}
return payloadID, nil
}
// notifyForkchoiceUpdate signals execution engine on a new payload
func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, header *ethpb.ExecutionPayloadHeader, postState state.BeaconState, blk block.SignedBeaconBlock) error {
func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, header *ethpb.ExecutionPayloadHeader, postState state.BeaconState, blk block.SignedBeaconBlock, root [32]byte) error {
ctx, span := trace.StartSpan(ctx, "blockChain.notifyNewPayload")
defer span.End()
if postState == nil {
return errors.New("pre and post states must not be nil")
}
// Execution payload is only supported in Bellatrix and beyond.
// Execution payload is only supported in Bellatrix and beyond. Pre
// merge blocks are never optimistic
if isPreBellatrix(postState.Version()) {
return nil
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
}
if err := helpers.BeaconBlockIsNil(blk); err != nil {
return err
@@ -100,7 +108,7 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
return errors.Wrap(err, "could not determine if execution is enabled")
}
if !enabled {
return nil
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
}
payload, err := body.ExecutionPayload()
if err != nil {
@@ -120,6 +128,10 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
}
}
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root); err != nil {
return errors.Wrap(err, "could not set optimistic status")
}
// During the transition event, the transition block should be verified for sanity.
if isPreBellatrix(preStateVersion) {
// Handle case where pre-state is Altair but block contains payload.
@@ -145,14 +157,38 @@ func isPreBellatrix(v int) bool {
//
// Spec pseudocode definition:
// def is_optimistic_candidate_block(opt_store: OptimisticStore, current_slot: Slot, block: BeaconBlock) -> bool:
// justified_root = opt_store.block_states[opt_store.head_block_root].current_justified_checkpoint.root
// justified_is_execution_block = is_execution_block(opt_store.blocks[justified_root])
// block_is_deep = block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot
// return justified_is_execution_block or block_is_deep
// if is_execution_block(opt_store.blocks[block.parent_root]):
// return True
//
// justified_root = opt_store.block_states[opt_store.head_block_root].current_justified_checkpoint.root
// if is_execution_block(opt_store.blocks[justified_root]):
// return True
//
// if block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot:
// return True
//
// return False
func (s *Service) optimisticCandidateBlock(ctx context.Context, blk block.BeaconBlock) (bool, error) {
if blk.Slot()+params.BeaconConfig().SafeSlotsToImportOptimistically <= s.CurrentSlot() {
return true, nil
}
parent, err := s.cfg.BeaconDB.Block(ctx, bytesutil.ToBytes32(blk.ParentRoot()))
if err != nil {
return false, err
}
if parent == nil {
return false, errNilParentInDB
}
parentIsExecutionBlock, err := blocks.ExecutionBlock(parent.Block().Body())
if err != nil {
return false, err
}
if parentIsExecutionBlock {
return true, nil
}
j := s.store.JustifiedCheckpt()
if j == nil {
return false, errNilJustifiedInStore

View File

@@ -44,7 +44,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
}
service, err := NewService(ctx, opts...)
require.NoError(t, err)
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, 0, 0))
require.NoError(t, fcs.InsertOptimisticBlock(ctx, 0, [32]byte{}, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
tests := []struct {
name string
@@ -155,7 +155,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
engine := &mockEngineService{forkchoiceError: tt.newForkchoiceErr}
service.cfg.ExecutionEngineCaller = engine
_, err := service.notifyForkchoiceUpdate(ctx, tt.blk, tt.finalizedRoot)
_, err := service.notifyForkchoiceUpdate(ctx, tt.blk, service.headRoot(), tt.finalizedRoot)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -346,7 +346,9 @@ func Test_NotifyNewPayload(t *testing.T) {
payload, err = tt.preState.LatestExecutionPayloadHeader()
require.NoError(t, err)
}
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
root := [32]byte{'a'}
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, root, root, params.BeaconConfig().ZeroHash, 0, 0))
err = service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk, root)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -356,6 +358,53 @@ func Test_NotifyNewPayload(t *testing.T) {
}
}
func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
cfg := params.BeaconConfig()
cfg.TerminalTotalDifficulty = "2"
params.OverrideBeaconConfig(cfg)
ctx := context.Background()
beaconDB := testDB.SetupDB(t)
fcs := protoarray.New(0, 0, [32]byte{'a'})
opts := []Option{
WithDatabase(beaconDB),
WithStateGen(stategen.New(beaconDB)),
WithForkChoiceStore(fcs),
}
bellatrixState, _ := util.DeterministicGenesisStateBellatrix(t, 2)
blk := &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
},
},
},
}
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
service, err := NewService(ctx, opts...)
require.NoError(t, err)
engine := &mockEngineService{blks: map[[32]byte]*v1.ExecutionBlock{}}
engine.blks[[32]byte{'a'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
TotalDifficulty: "0x2",
}
engine.blks[[32]byte{'b'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
TotalDifficulty: "0x1",
}
service.cfg.ExecutionEngineCaller = engine
payload, err := bellatrixState.LatestExecutionPayloadHeader()
require.NoError(t, err)
root := [32]byte{'c'}
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 1, root, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 0, 0))
err = service.notifyNewPayload(ctx, bellatrixState.Version(), payload, bellatrixState, bellatrixBlk, root)
require.NoError(t, err)
optimistic, err := service.IsOptimisticForRoot(ctx, root)
require.NoError(t, err)
require.Equal(t, false, optimistic)
}
func Test_IsOptimisticCandidateBlock(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MainnetConfig())
@@ -375,6 +424,12 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
params.BeaconConfig().SafeSlotsToImportOptimistically = 128
service.genesisTime = time.Now().Add(-time.Second * 12 * 2 * 128)
parentBlk := util.NewBeaconBlockBellatrix()
wrappedParentBlock, err := wrapper.WrappedBellatrixSignedBeaconBlock(parentBlk)
require.NoError(t, err)
parentRoot, err := wrappedParentBlock.Block().HashTreeRoot()
require.NoError(t, err)
tests := []struct {
name string
blk block.BeaconBlock
@@ -386,6 +441,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
blk: func(tt *testing.T) block.BeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 1
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
require.NoError(tt, err)
return wr
@@ -393,6 +449,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
justified: func(tt *testing.T) block.SignedBeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 32
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
require.NoError(tt, err)
return wr
@@ -404,6 +461,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
blk: func(tt *testing.T) block.BeaconBlock {
blk := util.NewBeaconBlockAltair()
blk.Block.Slot = 200
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedAltairBeaconBlock(blk.Block)
require.NoError(tt, err)
return wr
@@ -411,6 +469,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
justified: func(tt *testing.T) block.SignedBeaconBlock {
blk := util.NewBeaconBlockAltair()
blk.Block.Slot = 32
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
require.NoError(tt, err)
return wr
@@ -422,6 +481,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
blk: func(tt *testing.T) block.BeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 200
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
require.NoError(tt, err)
return wr
@@ -429,6 +489,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
justified: func(tt *testing.T) block.SignedBeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 32
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
require.NoError(tt, err)
return wr
@@ -440,6 +501,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
blk: func(tt *testing.T) block.BeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 200
blk.Block.ParentRoot = parentRoot[:]
wr, err := wrapper.WrappedBellatrixBeaconBlock(blk.Block)
require.NoError(tt, err)
return wr
@@ -447,6 +509,7 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
justified: func(tt *testing.T) block.SignedBeaconBlock {
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = 32
blk.Block.ParentRoot = parentRoot[:]
blk.Block.Body.ExecutionPayload.ParentHash = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
blk.Block.Body.ExecutionPayload.FeeRecipient = bytesutil.PadTo([]byte{'a'}, fieldparams.FeeRecipientLength)
blk.Block.Body.ExecutionPayload.StateRoot = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
@@ -471,8 +534,59 @@ func Test_IsOptimisticCandidateBlock(t *testing.T) {
Root: jroot[:],
Epoch: slots.ToEpoch(tt.justified.Block().Slot()),
})
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrappedParentBlock))
candidate, err := service.optimisticCandidateBlock(ctx, tt.blk)
require.NoError(t, err)
require.Equal(t, tt.want, candidate, tt.name)
}
}
func Test_IsOptimisticShallowExecutionParent(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MainnetConfig())
ctx := context.Background()
beaconDB := testDB.SetupDB(t)
opts := []Option{
WithDatabase(beaconDB),
WithStateGen(stategen.New(beaconDB)),
}
service, err := NewService(ctx, opts...)
require.NoError(t, err)
params.BeaconConfig().SafeSlotsToImportOptimistically = 128
service.genesisTime = time.Now().Add(-time.Second * 12 * 2 * 128)
payload := &v1.ExecutionPayload{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32),
ReceiptsRoot: make([]byte, 32),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
BlockHash: make([]byte, 32),
BlockNumber: 100,
}
body := &ethpb.BeaconBlockBodyBellatrix{ExecutionPayload: payload}
block := &ethpb.BeaconBlockBellatrix{Body: body, Slot: 200}
rawSigned := &ethpb.SignedBeaconBlockBellatrix{Block: block}
blk := util.HydrateSignedBeaconBlockBellatrix(rawSigned)
wr, err := wrapper.WrappedBellatrixSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wr))
blkRoot, err := wr.Block().HashTreeRoot()
require.NoError(t, err)
childBlock := util.NewBeaconBlockBellatrix()
childBlock.Block.ParentRoot = blkRoot[:]
// shallow block
childBlock.Block.Slot = 201
wrappedChild, err := wrapper.WrappedBellatrixSignedBeaconBlock(childBlock)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrappedChild))
candidate, err := service.optimisticCandidateBlock(ctx, wrappedChild.Block())
require.NoError(t, err)
require.Equal(t, true, candidate)
}

View File

@@ -256,7 +256,7 @@ func TestStore_OnAttestation_Ok_ProtoArray(t *testing.T) {
copied, err = transition.ProcessSlots(ctx, copied, 1)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, service.OnAttestation(ctx, att[0]))
}
@@ -282,7 +282,7 @@ func TestStore_OnAttestation_Ok_DoublyLinkedTree(t *testing.T) {
copied, err = transition.ProcessSlots(ctx, copied, 1)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, service.OnAttestation(ctx, att[0]))
}
@@ -571,8 +571,8 @@ func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) {
r33, err := b33.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b32.Block.Slot, r32, [32]byte{}, 0, 0))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b33.Block.Slot, r33, r32, 0, 0))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b32.Block.Slot, r32, [32]byte{}, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, b33.Block.Slot, r33, r32, params.BeaconConfig().ZeroHash, 0, 0))
_, err = service.cfg.ForkChoiceStore.Head(ctx, 0, r32, []uint64{}, 0)
require.NoError(t, err)

View File

@@ -107,13 +107,12 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
if err != nil {
return err
}
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, postState, signed); err != nil {
return errors.Wrap(err, "could not verify new payload")
}
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
return err
}
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, postState, signed, blockRoot); err != nil {
return errors.Wrap(err, "could not verify new payload")
}
// We add a proposer score boost to fork choice for the block root if applicable, right after
// running a successful state transition for the block.
@@ -182,7 +181,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
if err := s.updateHead(ctx, balances); err != nil {
log.WithError(err).Warn("Could not update head")
}
if _, err := s.notifyForkchoiceUpdate(ctx, s.headBlock().Block(), bytesutil.ToBytes32(finalized.Root)); err != nil {
if _, err := s.notifyForkchoiceUpdate(ctx, s.headBlock().Block(), s.headRoot(), bytesutil.ToBytes32(finalized.Root)); err != nil {
return err
}
@@ -280,6 +279,11 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
if len(blks) == 0 || len(blockRoots) == 0 {
return nil, nil, errors.New("no blocks provided")
}
if len(blks) != len(blockRoots) {
return nil, nil, errWrongBlockCount
}
if err := helpers.BeaconBlockIsNil(blks[0]); err != nil {
return nil, nil, err
}
@@ -314,9 +318,6 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
// Save potential boundary states.
if slots.IsEpochStart(preState.Slot()) {
boundaries[blockRoots[i]] = preState.Copy()
if err := s.handleEpochBoundary(ctx, preState); err != nil {
return nil, nil, errors.Wrap(err, "could not handle epoch boundary state")
}
}
jCheckpoints[i] = preState.CurrentJustifiedCheckpoint()
fCheckpoints[i] = preState.FinalizedCheckpoint()
@@ -329,6 +330,25 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
if !verify {
return nil, nil, errors.New("batch block signature verification failed")
}
// blocks have been verified, add them to forkchoice and call the engine
for i, b := range blks {
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
if err != nil {
return nil, nil, err
}
s.saveInitSyncBlock(blockRoots[i], b)
if err := s.insertBlockToForkChoiceStore(ctx, b.Block(), blockRoots[i], fCheckpoints[i], jCheckpoints[i]); err != nil {
return nil, nil, err
}
if err := s.notifyNewPayload(ctx, preStateVersion, preStateHeader, preState, b, blockRoots[i]); err != nil {
return nil, nil, err
}
if _, err := s.notifyForkchoiceUpdate(ctx, b.Block(), blockRoots[i], bytesutil.ToBytes32(fCheckpoints[i].Root)); err != nil {
return nil, nil, err
}
}
for r, st := range boundaries {
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
return nil, nil, err
@@ -350,16 +370,6 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []block.SignedBeaconBlo
// their state summaries and split them off to relative hot/cold storage.
func (s *Service) handleBlockAfterBatchVerify(ctx context.Context, signed block.SignedBeaconBlock,
blockRoot [32]byte, fCheckpoint, jCheckpoint *ethpb.Checkpoint) error {
b := signed.Block()
s.saveInitSyncBlock(blockRoot, signed)
if err := s.insertBlockToForkChoiceStore(ctx, b, blockRoot, fCheckpoint, jCheckpoint); err != nil {
return err
}
// TODO(10261) send optimistic status
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
return err
}
if err := s.cfg.BeaconDB.SaveStateSummary(ctx, &ethpb.StateSummary{
Slot: signed.Block().Slot(),
@@ -475,14 +485,27 @@ func (s *Service) insertBlockToForkChoiceStore(ctx context.Context, blk block.Be
return err
}
// Feed in block to fork choice store.
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()),
jCheckpoint.Epoch,
fCheckpoint.Epoch); err != nil {
return errors.Wrap(err, "could not process block for proto array fork choice")
payloadHash, err := getBlockPayloadHash(blk)
if err != nil {
return err
}
// TODO(10261) send optimistic status
return s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, root)
return s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
blk.Slot(), root, bytesutil.ToBytes32(blk.ParentRoot()), payloadHash,
jCheckpoint.Epoch,
fCheckpoint.Epoch)
}
func getBlockPayloadHash(blk block.BeaconBlock) ([32]byte, error) {
payloadHash := [32]byte{}
if isPreBellatrix(blk.Version()) {
return payloadHash, nil
}
payload, err := blk.Body().ExecutionPayload()
if err != nil {
return payloadHash, err
}
return bytesutil.ToBytes32(payload.BlockHash), nil
}
// This saves post state info to DB or cache. This also saves post state info to fork choice store.

View File

@@ -366,16 +366,16 @@ func (s *Service) fillInForkChoiceMissingBlocks(ctx context.Context, blk block.B
for i := len(pendingNodes) - 1; i >= 0; i-- {
b := pendingNodes[i]
r := pendingRoots[i]
payloadHash, err := getBlockPayloadHash(blk)
if err != nil {
return err
}
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()),
b.Slot(), r, bytesutil.ToBytes32(b.ParentRoot()), payloadHash,
jCheckpoint.Epoch,
fCheckpoint.Epoch); err != nil {
return errors.Wrap(err, "could not process block for proto array fork choice")
}
// TODO(10261) send optimistic status
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, r); err != nil {
return err
}
}
return nil
}

View File

@@ -295,6 +295,8 @@ func TestStore_OnBlockBatch_ProtoArray(t *testing.T) {
rBlock.Block.ParentRoot = gRoot[:]
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
_, _, err = service.onBlockBatch(ctx, blks, blkRoots[1:])
require.ErrorIs(t, errWrongBlockCount, err)
_, _, err = service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
require.NoError(t, err)
}
@@ -347,10 +349,62 @@ func TestStore_OnBlockBatch_DoublyLinkedTree(t *testing.T) {
rBlock.Block.ParentRoot = gRoot[:]
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
_, _, err = service.onBlockBatch(ctx, blks, blkRoots[1:])
require.ErrorIs(t, errWrongBlockCount, err)
_, _, err = service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
require.NoError(t, err)
}
func TestStore_OnBlockBatch_NotifyNewPayload(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)
genesis := blocks.NewGenesisBlock(params.BeaconConfig().ZeroHash[:])
assert.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesis)))
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
service.store.SetFinalizedCheckpt(&ethpb.Checkpoint{Root: gRoot[:]})
service.cfg.ForkChoiceStore = doublylinkedtree.New(0, 0)
service.saveInitSyncBlock(gRoot, wrapper.WrappedPhase0SignedBeaconBlock(genesis))
st, keys := util.DeterministicGenesisState(t, 64)
bState := st.Copy()
var blks []block.SignedBeaconBlock
var blkRoots [][32]byte
var firstState state.BeaconState
blkCount := 4
for i := 1; i <= blkCount; i++ {
b, err := util.GenerateFullBlock(bState, keys, util.DefaultBlockGenConfig(), types.Slot(i))
require.NoError(t, err)
bState, err = transition.ExecuteStateTransition(ctx, bState, wrapper.WrappedPhase0SignedBeaconBlock(b))
require.NoError(t, err)
if i == 1 {
firstState = bState.Copy()
}
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
service.saveInitSyncBlock(root, wrapper.WrappedPhase0SignedBeaconBlock(b))
blks = append(blks, wrapper.WrappedPhase0SignedBeaconBlock(b))
blkRoots = append(blkRoots, root)
}
rBlock, err := blks[0].PbPhase0Block()
assert.NoError(t, err)
rBlock.Block.ParentRoot = gRoot[:]
require.NoError(t, beaconDB.SaveBlock(context.Background(), blks[0]))
require.NoError(t, service.cfg.StateGen.SaveState(ctx, blkRoots[0], firstState))
cp1, cp2, err := service.onBlockBatch(ctx, blks[1:], blkRoots[1:])
require.NoError(t, err)
require.Equal(t, blkCount-1, len(cp1))
require.Equal(t, blkCount-1, len(cp2))
}
func TestRemoveStateSinceLastFinalized_EmptyStartSlot(t *testing.T) {
ctx := context.Background()
params.SetupTestConfigCleanup(t)
@@ -1035,7 +1089,7 @@ func TestAncestor_CanUseForkchoice(t *testing.T) {
beaconBlock.Block.ParentRoot = bytesutil.PadTo(b.Block.ParentRoot, 32)
r, err := b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), 0, 0)) // Saves blocks to fork choice store.
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), b.Block.Slot, r, bytesutil.ToBytes32(b.Block.ParentRoot), params.BeaconConfig().ZeroHash, 0, 0)) // Saves blocks to fork choice store.
}
r, err := service.ancestor(context.Background(), r200[:], 150)
@@ -1080,7 +1134,7 @@ func TestAncestor_CanUseDB(t *testing.T) {
require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(beaconBlock))) // Saves blocks to DB.
}
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), 200, r200, r200, 0, 0))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(context.Background(), 200, r200, r200, params.BeaconConfig().ZeroHash, 0, 0))
r, err := service.ancestor(context.Background(), r200[:], 150)
require.NoError(t, err)

View File

@@ -161,14 +161,36 @@ func (s *Service) spawnProcessAttestationsRoutine(stateFeed *event.Feed) {
log.WithError(err).Errorf("Unable to get justified balances for root %v", justified.Root)
continue
}
prevHead := s.headRoot()
if err := s.updateHead(s.ctx, balances); err != nil {
log.WithError(err).Warn("Resolving fork due to new attestation")
}
s.notifyEngineIfChangedHead(prevHead)
}
}
}()
}
// This calls notify Forkchoice Update in the event that the head has changed
func (s *Service) notifyEngineIfChangedHead(prevHead [32]byte) {
if s.headRoot() == prevHead {
return
}
finalized := s.store.FinalizedCheckpt()
if finalized == nil {
log.WithError(errNilFinalizedInStore).Error("could not get finalized checkpoint")
return
}
_, err := s.notifyForkchoiceUpdate(s.ctx,
s.headBlock().Block(),
s.headRoot(),
bytesutil.ToBytes32(finalized.Root),
)
if err != nil {
log.WithError(err).Error("could not notify forkchoice update")
}
}
// This processes fork choice attestations from the pool to account for validator votes and fork choice.
func (s *Service) processAttestations(ctx context.Context) {
atts := s.cfg.AttPool.ForkchoiceAttestations()

View File

@@ -111,9 +111,44 @@ func TestProcessAttestations_Ok(t *testing.T) {
copied, err = transition.ProcessSlots(ctx, copied, 1)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, 1, 1))
require.NoError(t, service.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx, 0, tRoot, tRoot, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, service.cfg.AttPool.SaveForkchoiceAttestations(atts))
service.processAttestations(ctx)
require.Equal(t, 0, len(service.cfg.AttPool.ForkchoiceAttestations()))
require.LogsDoNotContain(t, hook, "Could not process attestation for fork choice")
}
func TestNotifyEngineIfChangedHead(t *testing.T) {
hook := logTest.NewGlobal()
ctx := context.Background()
opts := testServiceOptsWithDB(t)
service, err := NewService(ctx, opts...)
require.NoError(t, err)
service.notifyEngineIfChangedHead(service.headRoot())
hookErr := "could not notify forkchoice update"
finalizedErr := "could not get finalized checkpoint"
require.LogsDoNotContain(t, hook, finalizedErr)
require.LogsDoNotContain(t, hook, hookErr)
service.notifyEngineIfChangedHead([32]byte{'a'})
require.LogsContain(t, hook, finalizedErr)
hook.Reset()
b := util.NewBeaconBlock()
b.Block.Slot = 1
wr := wrapper.WrappedPhase0SignedBeaconBlock(b)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wr))
r, err := b.Block.HashTreeRoot()
require.NoError(t, err)
finalized := &ethpb.Checkpoint{Root: r[:], Epoch: 0}
service.head = &head{
slot: 1,
root: r,
block: wr,
}
service.store.SetFinalizedCheckpt(finalized)
service.notifyEngineIfChangedHead([32]byte{'b'})
require.LogsDoNotContain(t, hook, finalizedErr)
require.LogsDoNotContain(t, hook, hookErr)
}

View File

@@ -64,7 +64,9 @@ func (s *Service) ReceiveBlock(ctx context.Context, block block.SignedBeaconBloc
return err
}
// Log state transition data.
logStateTransitionData(blockCopy.Block())
if err := logStateTransitionData(blockCopy.Block()); err != nil {
return err
}
return nil
}

View File

@@ -132,6 +132,7 @@ func TestService_ReceiveBlock(t *testing.T) {
WithExitPool(voluntaryexits.NewPool()),
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
WithStateGen(stategen.New(beaconDB)),
WithFinalizedStateAtStartUp(genesis),
}
s, err := NewService(ctx, opts...)
require.NoError(t, err)

View File

@@ -3,6 +3,7 @@
package blockchain
import (
"bytes"
"context"
"fmt"
"runtime"
@@ -191,21 +192,44 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
s.store = store.New(justified, finalized)
var store f.ForkChoicer
fRoot := bytesutil.ToBytes32(finalized.Root)
if features.Get().EnableForkChoiceDoublyLinkedTree {
store = doublylinkedtree.New(justified.Epoch, finalized.Epoch)
} else {
store = protoarray.New(justified.Epoch, finalized.Epoch, bytesutil.ToBytes32(finalized.Root))
store = protoarray.New(justified.Epoch, finalized.Epoch, fRoot)
}
s.cfg.ForkChoiceStore = store
ss, err := slots.EpochStart(finalized.Epoch)
fb, err := s.cfg.BeaconDB.Block(s.ctx, s.ensureRootNotZeros(fRoot))
if err != nil {
return errors.Wrap(err, "could not get start slot of finalized epoch")
return errors.Wrap(err, "could not get finalized checkpoint block")
}
if fb == nil {
return errNilFinalizedInStore
}
payloadHash, err := getBlockPayloadHash(fb.Block())
if err != nil {
return errors.Wrap(err, "could not get execution payload hash")
}
fSlot := fb.Block().Slot()
if err := store.InsertOptimisticBlock(s.ctx, fSlot, fRoot, params.BeaconConfig().ZeroHash,
payloadHash, justified.Epoch, finalized.Epoch); err != nil {
return errors.Wrap(err, "could not insert finalized block to forkchoice")
}
lastValidatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(s.ctx)
if err != nil {
return errors.Wrap(err, "could not get last validated checkpoint")
}
if bytes.Equal(finalized.Root, lastValidatedCheckpoint.Root) {
if err := store.SetOptimisticToValid(s.ctx, fRoot); err != nil {
return errors.Wrap(err, "could not set finalized block as validated")
}
}
h := s.headBlock().Block()
if h.Slot() > ss {
if h.Slot() > fSlot {
log.WithFields(logrus.Fields{
"startSlot": ss,
"startSlot": fSlot,
"endSlot": h.Slot(),
}).Info("Loading blocks to fork choice store, this may take a while.")
if err := s.fillInForkChoiceMissingBlocks(s.ctx, h, finalized, justified); err != nil {
@@ -449,10 +473,15 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState state.Beacon
genesisCheckpoint := genesisState.FinalizedCheckpoint()
s.store = store.New(genesisCheckpoint, genesisCheckpoint)
payloadHash, err := getBlockPayloadHash(genesisBlk.Block())
if err != nil {
return err
}
if err := s.cfg.ForkChoiceStore.InsertOptimisticBlock(ctx,
genesisBlk.Block().Slot(),
genesisBlkRoot,
params.BeaconConfig().ZeroHash,
payloadHash,
genesisCheckpoint.Epoch,
genesisCheckpoint.Epoch); err != nil {
log.Fatalf("Could not process genesis block for fork choice: %v", err)

View File

@@ -153,6 +153,11 @@ func TestChainStartStop_Initialized(t *testing.T) {
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, &ethpb.Checkpoint{Root: blkRoot[:]}))
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: blkRoot[:]}))
ss := &ethpb.StateSummary{
Slot: 1,
Root: blkRoot[:],
}
require.NoError(t, beaconDB.SaveStateSummary(ctx, ss))
chainService.cfg.FinalizedStateAtStartUp = s
// Test the start function.
chainService.Start()
@@ -179,7 +184,9 @@ func TestChainStartStop_GenesisZeroHashes(t *testing.T) {
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, s, blkRoot))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, blkRoot))
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesisBlk)))
require.NoError(t, beaconDB.SaveJustifiedCheckpoint(ctx, &ethpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}))
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: blkRoot[:]}))
chainService.cfg.FinalizedStateAtStartUp = s
// Test the start function.
chainService.Start()
@@ -288,8 +295,10 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
stateGen := stategen.New(beaconDB)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
require.NoError(t, err)
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
require.NoError(t, c.StartFromSavedState(headState))
headBlk, err := c.HeadBlock(ctx)
require.NoError(t, err)
@@ -331,14 +340,22 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(headBlock)))
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}))
ss := &ethpb.StateSummary{
Slot: finalizedSlot,
Root: headRoot[:],
}
require.NoError(t, beaconDB.SaveStateSummary(ctx, ss))
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: headRoot[:], Epoch: slots.ToEpoch(finalizedSlot)}))
stateGen := stategen.New(beaconDB)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
require.NoError(t, err)
require.NoError(t, c.StartFromSavedState(headState))
s, err := c.HeadState(ctx)
require.NoError(t, err)
assert.DeepSSZEqual(t, headState.InnerStateUnsafe(), s.InnerStateUnsafe(), "Head state incorrect")
assert.Equal(t, genesisRoot, c.originBlockRoot, "Genesis block root incorrect")
assert.DeepEqual(t, genesis, c.head.block.Proto())
assert.DeepEqual(t, headBlock, c.head.block.Proto())
}
func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
@@ -380,9 +397,8 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
require.NoError(t, err)
require.NoError(t, headState.SetSlot(headBlock.Block.Slot))
require.NoError(t, headState.SetGenesisValidatorsRoot(params.BeaconConfig().ZeroHash[:]))
require.NoError(t, beaconDB.SaveState(ctx, headState, genesisRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, finalizedRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, headRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, finalizedRoot))
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, headRoot))
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{
Epoch: slots.ToEpoch(finalizedBlock.Block.Slot),
@@ -391,7 +407,8 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
attSrv, err := attestations.NewService(ctx, &attestations.Config{})
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
stateGen := stategen.New(beaconDB)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stateGen), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
require.NoError(t, err)
require.NoError(t, c.StartFromSavedState(headState))
s, err := c.HeadState(ctx)

View File

@@ -104,34 +104,30 @@ func SetParticipationAndRewardProposer(
targetEpoch types.Epoch,
indices []uint64,
participatedFlags map[uint8]bool, totalBalance uint64) (state.BeaconState, error) {
var epochParticipation []byte
var proposerRewardNumerator uint64
currentEpoch := time.CurrentEpoch(beaconState)
var err error
var stateErr error
if targetEpoch == currentEpoch {
epochParticipation, err = beaconState.CurrentEpochParticipation()
if err != nil {
return nil, err
}
stateErr = beaconState.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
if err != nil {
return nil, err
}
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
} else {
epochParticipation, err = beaconState.PreviousEpochParticipation()
if err != nil {
return nil, err
}
stateErr = beaconState.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
if err != nil {
return nil, err
}
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
}
proposerRewardNumerator, epochParticipation, err := EpochParticipation(beaconState, indices, epochParticipation, participatedFlags, totalBalance)
if err != nil {
return nil, err
}
if targetEpoch == currentEpoch {
if err := beaconState.SetCurrentParticipationBits(epochParticipation); err != nil {
return nil, err
}
} else {
if err := beaconState.SetPreviousParticipationBits(epochParticipation); err != nil {
return nil, err
}
if stateErr != nil {
return nil, stateErr
}
if err := RewardProposer(ctx, beaconState, proposerRewardNumerator); err != nil {

View File

@@ -15,29 +15,12 @@ go_library(
"weak_subjectivity.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
visibility = [
"//beacon-chain:__subpackages__",
"//cmd/beacon-chain:__subpackages__",
"//contracts/deposit:__pkg__",
"//crypto/keystore:__pkg__",
"//network/forks:__pkg__",
"//proto/prysm/v1alpha1:__subpackages__",
"//proto/prysm/v1alpha1/attestation:__pkg__",
"//runtime/interop:__pkg__",
"//slasher:__subpackages__",
"//testing/altair:__pkg__",
"//testing/benchmark/benchmark_files:__subpackages__",
"//testing/endtoend/evaluators:__pkg__",
"//testing/slasher/simulator:__pkg__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
"//tools:__subpackages__",
"//validator:__subpackages__",
],
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//container/trie:go_default_library",

View File

@@ -1,7 +1,6 @@
package helpers
import (
"bytes"
"context"
"encoding/hex"
"errors"
@@ -12,9 +11,11 @@ import (
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/math"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
v1alpha1 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/time/slots"
)
@@ -122,20 +123,20 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS
// current_epoch = compute_epoch_at_slot(get_current_slot(store))
// return current_epoch <= ws_state_epoch + ws_period
func IsWithinWeakSubjectivityPeriod(
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsCheckpoint *eth.WeakSubjectivityCheckpoint) (bool, error) {
ctx context.Context, currentEpoch types.Epoch, wsState state.ReadOnlyBeaconState, wsStateRoot [fieldparams.RootLength]byte, wsEpoch types.Epoch) (bool, error) {
// Make sure that incoming objects are not nil.
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil || wsCheckpoint == nil {
if wsState == nil || wsState.IsNil() || wsState.LatestBlockHeader() == nil {
return false, errors.New("invalid weak subjectivity state or checkpoint")
}
// Assert that state and checkpoint have the same root and epoch.
if !bytes.Equal(wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot) {
if bytesutil.ToBytes32(wsState.LatestBlockHeader().StateRoot) != wsStateRoot {
return false, fmt.Errorf("state (%#x) and checkpoint (%#x) roots do not match",
wsState.LatestBlockHeader().StateRoot, wsCheckpoint.StateRoot)
wsState.LatestBlockHeader().StateRoot, wsStateRoot)
}
if slots.ToEpoch(wsState.Slot()) != wsCheckpoint.Epoch {
if slots.ToEpoch(wsState.Slot()) != wsEpoch {
return false, fmt.Errorf("state (%v) and checkpoint (%v) epochs do not match",
slots.ToEpoch(wsState.Slot()), wsCheckpoint.Epoch)
slots.ToEpoch(wsState.Slot()), wsEpoch)
}
// Compare given epoch to state epoch + weak subjectivity period.
@@ -164,7 +165,7 @@ func LatestWeakSubjectivityEpoch(ctx context.Context, st state.ReadOnlyBeaconSta
}
// ParseWeakSubjectivityInputString parses "blocks_root:epoch_number" string into a checkpoint.
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*eth.Checkpoint, error) {
func ParseWeakSubjectivityInputString(wsCheckpointString string) (*v1alpha1.Checkpoint, error) {
if wsCheckpointString == "" {
return nil, nil
}
@@ -197,7 +198,7 @@ func ParseWeakSubjectivityInputString(wsCheckpointString string) (*eth.Checkpoin
return nil, err
}
return &eth.Checkpoint{
return &v1alpha1.Checkpoint{
Epoch: types.Epoch(epoch),
Root: bRoot,
}, nil

View File

@@ -54,12 +54,15 @@ func TestWeakSubjectivity_ComputeWeakSubjectivityPeriod(t *testing.T) {
})
}
}
type mockWsCheckpoint func() (stateRoot [32]byte, blockRoot [32]byte, e types.Epoch)
func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
tests := []struct {
name string
epoch types.Epoch
genWsState func() state.ReadOnlyBeaconState
genWsCheckpoint func() *ethpb.WeakSubjectivityCheckpoint
genWsCheckpoint mockWsCheckpoint
want bool
wantedErr string
}{
@@ -68,22 +71,8 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
genWsState: func() state.ReadOnlyBeaconState {
return nil
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
BlockRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
Epoch: 42,
}
},
wantedErr: "invalid weak subjectivity state or checkpoint",
},
{
name: "nil weak subjectivity checkpoint",
genWsState: func() state.ReadOnlyBeaconState {
return genState(t, 128, 32)
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return nil
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
return [32]byte{}, [32]byte{}, 42
},
wantedErr: "invalid weak subjectivity state or checkpoint",
},
@@ -99,11 +88,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32),
Epoch: 42,
}
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot2"), 32))
return sr, [32]byte{}, 42
},
wantedErr: fmt.Sprintf("state (%#x) and checkpoint (%#x) roots do not match",
bytesutil.PadTo([]byte("stateroot1"), 32), bytesutil.PadTo([]byte("stateroot2"), 32)),
@@ -120,11 +108,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
Epoch: 43,
}
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 43
},
wantedErr: "state (42) and checkpoint (43) epochs do not match",
},
@@ -140,11 +127,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
Epoch: 42,
}
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
wantedErr: "cannot compute weak subjectivity period: no active validators found",
},
@@ -161,11 +147,10 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
Epoch: 42,
}
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
want: false,
},
@@ -182,18 +167,18 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint {
return &ethpb.WeakSubjectivityCheckpoint{
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
Epoch: 42,
}
genWsCheckpoint: func() ([32]byte, [32]byte, types.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), tt.genWsCheckpoint())
sr, _, e := tt.genWsCheckpoint()
got, err := helpers.IsWithinWeakSubjectivityPeriod(context.Background(), tt.epoch, tt.genWsState(), sr, e)
if tt.wantedErr != "" {
assert.Equal(t, false, got)
assert.ErrorContains(t, tt.wantedErr, err)

View File

@@ -245,7 +245,7 @@ func createFullBellatrixBlockWithOperations(t *testing.T) (state.BeaconState,
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BaseFeePerGas: bytesutil.PadTo([]byte{1, 2, 3, 4}, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
ExtraData: make([]byte, 0),

View File

@@ -45,6 +45,7 @@ type ReadOnlyDatabase interface {
HasArchivedPoint(ctx context.Context, slot types.Slot) bool
LastArchivedRoot(ctx context.Context) [32]byte
LastArchivedSlot(ctx context.Context) (types.Slot, error)
LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
// Deposit contract related handlers.
DepositContractAddress(ctx context.Context) ([]byte, error)
// Powchain operations.
@@ -74,6 +75,7 @@ type NoHeadAccessDatabase interface {
// Checkpoint operations.
SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
SaveLastValidatedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
// Deposit contract related handlers.
SaveDepositContractAddress(ctx context.Context, addr common.Address) error
// Powchain operations.

View File

@@ -25,6 +25,7 @@ go_library(
"state_summary.go",
"state_summary_cache.go",
"utils.go",
"validated_checkpoint.go",
"wss.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kv",
@@ -46,6 +47,7 @@ go_library(
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz/detect:go_default_library",
"//io/file:go_default_library",
"//monitoring/progress:go_default_library",
"//monitoring/tracing:go_default_library",
@@ -91,6 +93,7 @@ go_test(
"state_summary_test.go",
"state_test.go",
"utils_test.go",
"validated_checkpoint_test.go",
],
data = glob(["testdata/**"]),
embed = [":go_default_library"],

View File

@@ -175,7 +175,6 @@ func NewKVStore(ctx context.Context, dirPath string, config *Config) (*Store, er
powchainBucket,
stateSummaryBucket,
stateValidatorsBucket,
validatedTips,
// Indices buckets.
attestationHeadBlockRootBucket,
attestationSourceRootIndicesBucket,

View File

@@ -19,7 +19,6 @@ var (
powchainBucket = []byte("powchain")
stateValidatorsBucket = []byte("state-validators")
feeRecipientBucket = []byte("fee-recipient")
validatedTips = []byte("validated-synced-tips")
// Deprecated: This bucket was migrated in PR 6461. Do not use, except for migrations.
slotsHasObjectBucket = []byte("slots-has-objects")
@@ -39,12 +38,13 @@ var (
blockRootValidatorHashesBucket = []byte("block-root-validator-hashes")
// Specific item keys.
headBlockRootKey = []byte("head-root")
genesisBlockRootKey = []byte("genesis-root")
depositContractAddressKey = []byte("deposit-contract")
justifiedCheckpointKey = []byte("justified-checkpoint")
finalizedCheckpointKey = []byte("finalized-checkpoint")
powchainDataKey = []byte("powchain-data")
headBlockRootKey = []byte("head-root")
genesisBlockRootKey = []byte("genesis-root")
depositContractAddressKey = []byte("deposit-contract")
justifiedCheckpointKey = []byte("justified-checkpoint")
finalizedCheckpointKey = []byte("finalized-checkpoint")
powchainDataKey = []byte("powchain-data")
lastValidatedCheckpointKey = []byte("last-validated-checkpoint")
// Below keys are used to identify objects are to be fork compatible.
// Objects that are only compatible with specific forks should be prefixed with such keys.

View File

@@ -0,0 +1,49 @@
package kv
import (
"context"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
// LastValidatedCheckpoint returns the latest fully validated checkpoint in beacon chain.
func (s *Store) LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.LastValidatedCheckpoint")
defer span.End()
var checkpoint *ethpb.Checkpoint
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(checkpointBucket)
enc := bkt.Get(lastValidatedCheckpointKey)
if enc == nil {
var finErr error
checkpoint, finErr = s.FinalizedCheckpoint(ctx)
return finErr
}
checkpoint = &ethpb.Checkpoint{}
return decode(ctx, enc, checkpoint)
})
return checkpoint, err
}
// SaveLastValidatedCheckpoint saves the last validated checkpoint in beacon chain.
func (s *Store) SaveLastValidatedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveLastValidatedCheckpoint")
defer span.End()
enc, err := encode(ctx, checkpoint)
if err != nil {
return err
}
return s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(checkpointBucket)
hasStateSummary := s.hasStateSummaryBytes(tx, bytesutil.ToBytes32(checkpoint.Root))
hasStateInDB := tx.Bucket(stateBucket).Get(checkpoint.Root) != nil
if !(hasStateInDB || hasStateSummary) {
return errMissingStateForCheckpoint
}
return bucket.Put(lastValidatedCheckpointKey, enc)
})
}

View File

@@ -0,0 +1,67 @@
package kv
import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"google.golang.org/protobuf/proto"
)
func TestStore_LastValidatedCheckpoint_CanSaveRetrieve(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
root := bytesutil.ToBytes32([]byte{'A'})
cp := &ethpb.Checkpoint{
Epoch: 10,
Root: root[:],
}
st, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, st.SetSlot(1))
require.NoError(t, db.SaveState(ctx, st, root))
require.NoError(t, db.SaveLastValidatedCheckpoint(ctx, cp))
retrieved, err := db.LastValidatedCheckpoint(ctx)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}
func TestStore_LastValidatedCheckpoint_DefaultIsFinalized(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'})
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesis))
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = genesis[:]
blk.Block.Slot = 40
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
cp := &ethpb.Checkpoint{
Epoch: 5,
Root: root[:],
}
// a valid chain is required to save finalized checkpoint.
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(blk)))
st, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, st.SetSlot(1))
// a state is required to save checkpoint
require.NoError(t, db.SaveState(ctx, st, root))
require.NoError(t, db.SaveFinalizedCheckpoint(ctx, cp))
retrieved, err := db.LastValidatedCheckpoint(ctx)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(cp, retrieved), "Wanted %v, received %v", cp, retrieved)
}

View File

@@ -7,10 +7,9 @@ import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
statev2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/ssz/detect"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
)
// SaveOrigin loads an ssz serialized Block & BeaconState from an io.Reader
@@ -18,33 +17,36 @@ import (
// syncing, using the provided values as their point of origin. This is an alternative
// to syncing from genesis, and should only be run on an empty database.
func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Reader) error {
// unmarshal both block and state before trying to save anything
// so that we fail early if there is any issue with the ssz data
blk := &ethpb.SignedBeaconBlockAltair{}
sb, err := ioutil.ReadAll(stateReader)
if err != nil {
return errors.Wrap(err, "failed to read origin state bytes")
}
bb, err := ioutil.ReadAll(blockReader)
if err != nil {
return errors.Wrap(err, "error reading block given to SaveOrigin")
}
if err := blk.UnmarshalSSZ(bb); err != nil {
return errors.Wrap(err, "could not unmarshal checkpoint block")
}
wblk, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
cf, err := detect.FromState(sb)
if err != nil {
return errors.Wrap(err, "could not wrap checkpoint block")
return errors.Wrap(err, "failed to detect config and fork for origin state")
}
bs, err := statev2.InitializeFromSSZReader(stateReader)
bs, err := cf.UnmarshalBeaconState(sb)
if err != nil {
return errors.Wrap(err, "could not initialize checkpoint state from reader")
return errors.Wrap(err, "could not unmarshal origin state")
}
wblk, err := cf.UnmarshalBeaconBlock(bb)
if err != nil {
return errors.Wrap(err, "unable to unmarshal origin SignedBeaconBlock")
}
blockRoot, err := wblk.Block().HashTreeRoot()
if err != nil {
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
}
// save block
if err := s.SaveBlock(ctx, wblk); err != nil {
return errors.Wrap(err, "could not save checkpoint block")
}
blockRoot, err := blk.Block.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
}
// save state
if err = s.SaveState(ctx, bs, blockRoot); err != nil {
@@ -70,7 +72,7 @@ func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Read
// rebuild the checkpoint from the block
// use it to mark the block as justified and finalized
slotEpoch, err := blk.Block.Slot.SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
slotEpoch, err := wblk.Block().Slot().SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
if err != nil {
return err
}

View File

@@ -2,7 +2,7 @@ package doublylinkedtree
import "errors"
var errNilNode = errors.New("invalid nil or unknown node")
var ErrNilNode = errors.New("invalid nil or unknown node")
var errInvalidBalance = errors.New("invalid node balance")
var errInvalidProposerBoostRoot = errors.New("invalid proposer boost root")
var errUnknownFinalizedRoot = errors.New("unknown finalized root")

View File

@@ -27,9 +27,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
// 2 <- justified: 1, finalized: 0
// |
// 3 <- justified: 2, finalized: 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 2, 1))
// With starting justified epoch at 0, the head should be 3:
// 0 <- start
@@ -89,17 +89,17 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
// | |
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
// Left branch.
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), 2, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0))
// Right branch.
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), 2, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0))
// With start at 0, the head should be 10:
// 0 <-- start
@@ -185,7 +185,7 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
func setup(justifiedEpoch, finalizedEpoch types.Epoch) *ForkChoice {
ctx := context.Background()
f := New(justifiedEpoch, finalizedEpoch)
err := f.InsertOptimisticBlock(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, justifiedEpoch, finalizedEpoch)
err := f.InsertOptimisticBlock(ctx, 0, params.BeaconConfig().ZeroHash, [32]byte{}, params.BeaconConfig().ZeroHash, justifiedEpoch, finalizedEpoch)
if err != nil {
return nil
}

View File

@@ -106,13 +106,13 @@ func (f *ForkChoice) ProcessAttestation(ctx context.Context, validatorIndices []
func (f *ForkChoice) InsertOptimisticBlock(
ctx context.Context,
slot types.Slot,
blockRoot, parentRoot [fieldparams.RootLength]byte,
blockRoot, parentRoot, payloadHash [fieldparams.RootLength]byte,
justifiedEpoch, finalizedEpoch types.Epoch,
) error {
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.InsertOptimisticBlock")
defer span.End()
return f.store.insert(ctx, slot, blockRoot, parentRoot, justifiedEpoch, finalizedEpoch)
return f.store.insert(ctx, slot, blockRoot, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
}
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
@@ -174,7 +174,7 @@ func (f *ForkChoice) IsOptimistic(_ context.Context, root [32]byte) (bool, error
node, ok := f.store.nodeByRoot[root]
if !ok || node == nil {
return false, errNilNode
return false, ErrNilNode
}
return node.optimistic, nil
@@ -190,7 +190,7 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types
node, ok := f.store.nodeByRoot[root]
if !ok || node == nil {
return nil, errNilNode
return nil, ErrNilNode
}
n := node
@@ -202,7 +202,7 @@ func (f *ForkChoice) AncestorRoot(ctx context.Context, root [32]byte, slot types
}
if n == nil {
return nil, errNilNode
return nil, ErrNilNode
}
return n.root[:], nil
@@ -237,7 +237,7 @@ func (f *ForkChoice) updateBalances(newBalances []uint64) error {
if ok && vote.nextRoot != params.BeaconConfig().ZeroHash {
// Protection against nil node
if nextNode == nil {
return errNilNode
return ErrNilNode
}
nextNode.balance += newBalance
}
@@ -246,7 +246,7 @@ func (f *ForkChoice) updateBalances(newBalances []uint64) error {
if ok && vote.currentRoot != params.BeaconConfig().ZeroHash {
// Protection against nil node
if currentNode == nil {
return errNilNode
return ErrNilNode
}
if currentNode.balance < oldBalance {
return errInvalidBalance
@@ -279,7 +279,7 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams
defer f.store.nodesLock.Unlock()
node, ok := f.store.nodeByRoot[root]
if !ok || node == nil {
return errNilNode
return ErrNilNode
}
return node.setNodeAndParentValidated(ctx)
}
@@ -302,6 +302,6 @@ func (f *ForkChoice) ForkChoiceNodes() []*pbrpc.ForkChoiceNode {
}
// SetOptimisticToInvalid removes a block with an invalid execution payload from fork choice store
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [fieldparams.RootLength]byte) error {
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [fieldparams.RootLength]byte) ([][32]byte, error) {
return f.store.removeNode(ctx, root)
}

View File

@@ -15,9 +15,9 @@ import (
func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
f.votes = []Vote{
{indexToHash(1), indexToHash(1), 0},
@@ -37,9 +37,9 @@ func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) {
func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
s := f.store
s.nodeByRoot[indexToHash(1)].balance = 100
s.nodeByRoot[indexToHash(2)].balance = 100
@@ -61,12 +61,12 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) {
func TestForkChoice_IsCanonical(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(4), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, indexToHash(6), indexToHash(5), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 1))
require.Equal(t, true, f.IsCanonical(params.BeaconConfig().ZeroHash))
require.Equal(t, false, f.IsCanonical(indexToHash(1)))
@@ -80,12 +80,12 @@ func TestForkChoice_IsCanonical(t *testing.T) {
func TestForkChoice_IsCanonicalReorg(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, [32]byte{'2'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, [32]byte{'4'}, [32]byte{'2'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, [32]byte{'5'}, [32]byte{'4'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 6, [32]byte{'6'}, [32]byte{'5'}, params.BeaconConfig().ZeroHash, 1, 1))
f.store.nodesLock.Lock()
f.store.nodeByRoot[[32]byte{'3'}].balance = 10
@@ -114,9 +114,9 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) {
func TestForkChoice_AncestorRoot(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(3), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
f.store.treeRootNode = f.store.nodeByRoot[indexToHash(1)]
f.store.treeRootNode.parent = nil
@@ -125,7 +125,7 @@ func TestForkChoice_AncestorRoot(t *testing.T) {
assert.Equal(t, bytesutil.ToBytes32(r), indexToHash(3))
_, err = f.AncestorRoot(ctx, indexToHash(3), 0)
assert.ErrorContains(t, errNilNode.Error(), err)
assert.ErrorContains(t, ErrNilNode.Error(), err)
root, err := f.AncestorRoot(ctx, indexToHash(3), 5)
require.NoError(t, err)
@@ -140,8 +140,8 @@ func TestForkChoice_AncestorRoot(t *testing.T) {
func TestForkChoice_AncestorEqualSlot(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 100)
require.NoError(t, err)
@@ -152,8 +152,8 @@ func TestForkChoice_AncestorEqualSlot(t *testing.T) {
func TestForkChoice_AncestorLowerSlot(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'1'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 200, [32]byte{'3'}, [32]byte{'1'}, params.BeaconConfig().ZeroHash, 1, 1))
r, err := f.AncestorRoot(ctx, [32]byte{'3'}, 150)
require.NoError(t, err)

View File

@@ -24,7 +24,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 0
// /
// 2 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -33,7 +33,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 0
// / \
// head -> 2 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -44,7 +44,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// head -> 2 1
// |
// 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -55,7 +55,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 2 1
// | |
// head -> 4 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
@@ -68,7 +68,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// head -> 4 3
// |
// 5 <- justified epoch = 2
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
@@ -107,7 +107,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 5
// |
// 6 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2")

View File

@@ -55,7 +55,7 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
hasViableDescendant := false
for _, child := range n.children {
if child == nil {
return errNilNode
return ErrNilNode
}
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch); err != nil {
return err

View File

@@ -13,9 +13,9 @@ import (
func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
// The updated balances of each node is 100
s := f.store
@@ -36,9 +36,9 @@ func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) {
func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
// The updated balances of each node is 100
s := f.store
@@ -63,7 +63,7 @@ func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
// Input child is not viable.
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 2, 3))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 2, 3))
// Verify parent's best child and best descendant are `none`.
s := f.store
@@ -76,7 +76,7 @@ func TestNode_UpdateBestDescendant_ViableChild(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
// Input child is best descendant
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
s := f.store
assert.Equal(t, 1, len(s.treeRootNode.children))
@@ -87,8 +87,8 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
// Input child is best descendant
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
s := f.store
s.nodeByRoot[indexToHash(1)].weight = 100
@@ -103,8 +103,8 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
// Input child is best descendant
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
s := f.store
s.nodeByRoot[indexToHash(1)].weight = 200
@@ -119,9 +119,9 @@ func TestNode_TestDepth(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
// Input child is best descendant
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
s := f.store
require.Equal(t, s.nodeByRoot[indexToHash(2)].depth(), uint64(2))
@@ -151,11 +151,11 @@ func TestNode_ViableForHead(t *testing.T) {
func TestNode_LeadsToViableHead(t *testing.T) {
f := setup(4, 3)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(3), 4, 3))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 4, 3))
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3))
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3))
@@ -172,13 +172,13 @@ func TestNode_SetFullyValidated(t *testing.T) {
// \
// -- 5 (true)
//
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.SetOptimisticToValid(ctx, params.BeaconConfig().ZeroHash))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.SetOptimisticToValid(ctx, indexToHash(1)))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(3), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 4, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 5, indexToHash(5), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
opt, err := f.IsOptimistic(ctx, indexToHash(5))
require.NoError(t, err)

View File

@@ -6,16 +6,17 @@ import (
// removeNode removes the node with the given root and all of its children
// from the Fork Choice Store.
func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
func (s *Store) removeNode(ctx context.Context, root [32]byte) ([][32]byte, error) {
s.nodesLock.Lock()
defer s.nodesLock.Unlock()
invalidRoots := make([][32]byte, 0)
node, ok := s.nodeByRoot[root]
if !ok || node == nil {
return errNilNode
return invalidRoots, ErrNilNode
}
if !node.optimistic || node.parent == nil {
return errInvalidOptimisticStatus
return invalidRoots, errInvalidOptimisticStatus
}
children := node.parent.children
if len(children) == 1 {
@@ -31,20 +32,21 @@ func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
}
}
}
return s.removeNodeAndChildren(ctx, node)
return s.removeNodeAndChildren(ctx, node, invalidRoots)
}
// removeNodeAndChildren removes `node` and all of its descendant from the Store
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node) error {
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRoots [][32]byte) ([][32]byte, error) {
var err error
for _, child := range node.children {
if ctx.Err() != nil {
return ctx.Err()
return invalidRoots, ctx.Err()
}
if err := s.removeNodeAndChildren(ctx, child); err != nil {
return err
if invalidRoots, err = s.removeNodeAndChildren(ctx, child, invalidRoots); err != nil {
return invalidRoots, err
}
}
invalidRoots = append(invalidRoots, node.root)
delete(s.nodeByRoot, node.root)
return nil
return invalidRoots, nil
}

View File

@@ -25,46 +25,55 @@ func TestPruneInvalid(t *testing.T) {
tests := []struct {
root [32]byte // the root of the new INVALID block
wantedNodeNumber int
wantedRoots [][32]byte
}{
{
[32]byte{'j'},
12,
[][32]byte{[32]byte{'j'}},
},
{
[32]byte{'c'},
4,
[][32]byte{[32]byte{'f'}, [32]byte{'e'}, [32]byte{'i'}, [32]byte{'h'}, [32]byte{'l'},
[32]byte{'k'}, [32]byte{'g'}, [32]byte{'d'}, [32]byte{'c'}},
},
{
[32]byte{'i'},
12,
[][32]byte{[32]byte{'i'}},
},
{
[32]byte{'h'},
11,
[][32]byte{[32]byte{'i'}, [32]byte{'h'}},
},
{
[32]byte{'g'},
8,
[][32]byte{[32]byte{'i'}, [32]byte{'h'}, [32]byte{'l'}, [32]byte{'k'}, [32]byte{'g'}},
},
}
for _, tc := range tests {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.store.removeNode(context.Background(), tc.root))
roots, err := f.store.removeNode(context.Background(), tc.root)
require.NoError(t, err)
require.DeepEqual(t, tc.wantedRoots, roots)
require.Equal(t, tc.wantedNodeNumber, f.NodeCount())
}
}

View File

@@ -46,6 +46,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -69,6 +70,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -94,6 +96,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -119,6 +122,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -188,6 +192,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
honestBlockSlot,
honestBlock,
zeroHash,
zeroHash,
jEpoch,
fEpoch,
),
@@ -204,6 +209,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
maliciouslyWithheldBlockSlot,
maliciouslyWithheldBlock,
zeroHash,
zeroHash,
jEpoch,
fEpoch,
),
@@ -252,6 +258,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
honestBlockSlot,
honestBlock,
zeroHash,
zeroHash,
jEpoch,
fEpoch,
),
@@ -270,6 +277,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
maliciouslyWithheldBlockSlot,
maliciouslyWithheldBlock,
zeroHash,
zeroHash,
jEpoch,
fEpoch,
),
@@ -325,6 +333,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
cSlot,
c,
a, // parent
zeroHash,
jEpoch,
fEpoch,
),
@@ -348,6 +357,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
bSlot,
b,
a, // parent
zeroHash,
jEpoch,
fEpoch,
),
@@ -371,6 +381,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
dSlot,
d,
b, // parent
zeroHash,
jEpoch,
fEpoch,
),

View File

@@ -96,7 +96,7 @@ func (s *Store) head(ctx context.Context, justifiedRoot [32]byte) ([32]byte, err
// It then updates the new node's parent with best child and descendant node.
func (s *Store) insert(ctx context.Context,
slot types.Slot,
root, parentRoot [fieldparams.RootLength]byte,
root, parentRoot, payloadHash [fieldparams.RootLength]byte,
justifiedEpoch, finalizedEpoch types.Epoch) error {
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
defer span.End()
@@ -118,6 +118,7 @@ func (s *Store) insert(ctx context.Context,
justifiedEpoch: justifiedEpoch,
finalizedEpoch: finalizedEpoch,
optimistic: true,
payloadHash: payloadHash,
}
s.nodeByRoot[root] = n

View File

@@ -33,14 +33,14 @@ func TestStore_FinalizedEpoch(t *testing.T) {
func TestStore_NodeCount(t *testing.T) {
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.Equal(t, 2, f.NodeCount())
}
func TestStore_NodeByRoot(t *testing.T) {
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
node0 := f.store.treeRootNode
node1 := node0.children[0]
node2 := node1.children[0]
@@ -61,7 +61,7 @@ func TestStore_NodeByRoot(t *testing.T) {
func TestForkChoice_HasNode(t *testing.T) {
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.Equal(t, true, f.HasNode(indexToHash(1)))
}
@@ -74,7 +74,7 @@ func TestStore_Head_UnknownJustifiedRoot(t *testing.T) {
func TestStore_Head_Itself(t *testing.T) {
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
// Since the justified node does not have a best descendant so the best node
// is itself.
@@ -85,10 +85,10 @@ func TestStore_Head_Itself(t *testing.T) {
func TestStore_Head_BestDescendant(t *testing.T) {
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(4), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
h, err := f.store.head(context.Background(), indexToHash(1))
require.NoError(t, err)
require.Equal(t, h, indexToHash(4))
@@ -97,9 +97,9 @@ func TestStore_Head_BestDescendant(t *testing.T) {
func TestStore_UpdateBestDescendant_ContextCancelled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
f := setup(0, 0)
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
cancel()
err := f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 0, 0)
err := f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0)
require.ErrorContains(t, "context canceled", err)
}
@@ -108,10 +108,12 @@ func TestStore_Insert(t *testing.T) {
treeRootNode := &Node{slot: 0, root: indexToHash(0)}
nodeByRoot := map[[32]byte]*Node{indexToHash(0): treeRootNode}
s := &Store{nodeByRoot: nodeByRoot, treeRootNode: treeRootNode}
require.NoError(t, s.insert(context.Background(), 100, indexToHash(100), indexToHash(0), 1, 1))
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")
assert.Equal(t, (*Node)(nil), treeRootNode.parent, "Incorrect parent")
assert.Equal(t, 1, len(treeRootNode.children), "Incorrect children number")
assert.Equal(t, payloadHash, treeRootNode.children[0].payloadHash, "Incorrect payload hash")
child := treeRootNode.children[0]
assert.Equal(t, types.Epoch(1), child.justifiedEpoch, "Incorrect justification")
assert.Equal(t, types.Epoch(1), child.finalizedEpoch, "Incorrect finalization")
@@ -132,9 +134,9 @@ func TestStore_Prune_LessThanThreshold(t *testing.T) {
numOfNodes := uint64(100)
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
for i := uint64(2); i < numOfNodes; i++ {
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
}
s := f.store
@@ -151,9 +153,9 @@ func TestStore_Prune_MoreThanThreshold(t *testing.T) {
numOfNodes := uint64(100)
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
for i := uint64(2); i < numOfNodes; i++ {
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
}
s := f.store
@@ -169,9 +171,9 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
numOfNodes := uint64(100)
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
for i := uint64(2); i < numOfNodes; i++ {
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, types.Slot(i), indexToHash(i), indexToHash(i-1), params.BeaconConfig().ZeroHash, 0, 0))
}
s := f.store
@@ -196,8 +198,8 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
func TestStore_Prune_NoDanglingBranch(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
f.store.pruneThreshold = 0
s := f.store
@@ -221,18 +223,18 @@ func TestStore_tips(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
expectedMap := map[[32]byte]types.Slot{
[32]byte{'f'}: 105,
[32]byte{'i'}: 106,
@@ -250,8 +252,8 @@ func TestStore_tips(t *testing.T) {
func TestStore_PruneMapsNodes(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
s := f.store
s.pruneThreshold = 0
@@ -263,9 +265,9 @@ func TestStore_PruneMapsNodes(t *testing.T) {
func TestStore_HasParent(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.Equal(t, false, f.HasParent(params.BeaconConfig().ZeroHash))
require.Equal(t, true, f.HasParent(indexToHash(1)))
require.Equal(t, true, f.HasParent(indexToHash(2)))

View File

@@ -35,6 +35,7 @@ type Store struct {
type Node struct {
slot types.Slot // slot of the block converted to the node.
root [fieldparams.RootLength]byte // root of the block converted to the node.
payloadHash [fieldparams.RootLength]byte // payloadHash of the block converted to the node.
parent *Node // parent index of this node.
children []*Node // the list of direct children of this Node
justifiedEpoch types.Epoch // justifiedEpoch of this node.

View File

@@ -22,7 +22,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 0
// /
// 2 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -32,7 +32,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 0
// / \
// head -> 2 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -62,7 +62,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// head -> 2 1
// |
// 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -98,7 +98,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 3
// |
// 4 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -114,7 +114,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4 <- head
// /
// 5 <- justified epoch = 2
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -130,7 +130,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4 <- head
// / \
// 5 6 <- justified epoch = 0
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
// Moved 2 votes to block 5:
// 0
@@ -142,7 +142,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4
// / \
// 2 votes-> 5 6
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
@@ -163,9 +163,9 @@ func TestVotes_CanFindHead(t *testing.T) {
// 8
// |
// 9
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -210,7 +210,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// / \
// 2 votes->9 10
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
require.NoError(t, err)
@@ -289,7 +289,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 9 10
// |
// head-> 11
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
require.NoError(t, err)

View File

@@ -33,6 +33,7 @@ type BlockProcessor interface {
slot types.Slot,
root [32]byte,
parentRoot [32]byte,
payloadHash [32]byte,
justifiedEpoch types.Epoch,
finalizedEpoch types.Epoch,
) error
@@ -70,5 +71,5 @@ type Getter interface {
// Setter allows to set forkchoice information
type Setter interface {
SetOptimisticToValid(context.Context, [fieldparams.RootLength]byte) error
SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte) error
SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte) ([][32]byte, error)
}

View File

@@ -5,7 +5,7 @@ import "errors"
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
var errUnknownJustifiedRoot = errors.New("unknown justified root")
var errInvalidNodeIndex = errors.New("node index is invalid")
var errUnknownNodeRoot = errors.New("unknown block root")
var ErrUnknownNodeRoot = errors.New("unknown block root")
var errInvalidJustifiedIndex = errors.New("justified index is invalid")
var errInvalidBestChildIndex = errors.New("best child index is invalid")
var errInvalidBestDescendantIndex = errors.New("best descendant index is invalid")

View File

@@ -27,9 +27,9 @@ func TestFFGUpdates_OneBranch(t *testing.T) {
// 2 <- justified: 1, finalized: 0
// |
// 3 <- justified: 2, finalized: 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(2), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(3), indexToHash(2), params.BeaconConfig().ZeroHash, 2, 1))
// With starting justified epoch at 0, the head should be 3:
// 0 <- start
@@ -89,17 +89,17 @@ func TestFFGUpdates_TwoBranches(t *testing.T) {
// | |
// justified: 2, finalized: 0 -> 9 10 <- justified: 2, finalized: 0
// Left branch.
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), 2, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(5), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(9), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 0))
// Right branch.
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), 2, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 1, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 2, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 3, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 0, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(8), indexToHash(6), params.BeaconConfig().ZeroHash, 1, 0))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 4, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 0))
// With start at 0, the head should be 10:
// 0 <-- start

View File

@@ -92,6 +92,7 @@ func copyNode(node *Node) *Node {
slot: node.slot,
root: copiedRoot,
parent: node.parent,
payloadHash: node.payloadHash,
justifiedEpoch: node.justifiedEpoch,
finalizedEpoch: node.finalizedEpoch,
weight: node.weight,

View File

@@ -24,7 +24,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 0
// /
// 2 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -33,7 +33,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 0
// / \
// head -> 2 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -44,7 +44,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// head -> 2 1
// |
// 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(2), r, "Incorrect head for with justified epoch at 1")
@@ -55,7 +55,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 2 1
// | |
// head -> 4 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
@@ -68,7 +68,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// head -> 4 3
// |
// 5 <- justified epoch = 2
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(4), r, "Incorrect head for with justified epoch at 1")
@@ -107,7 +107,7 @@ func TestNoVote_CanFindHead(t *testing.T) {
// 5
// |
// 6 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), 2, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 1))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 1)
require.NoError(t, err)
assert.Equal(t, indexToHash(6), r, "Incorrect head for with justified epoch at 2")

View File

@@ -34,13 +34,11 @@ func (f *ForkChoice) IsOptimistic(ctx context.Context, root [32]byte) (bool, err
if ctx.Err() != nil {
return false, ctx.Err()
}
// If we reached this point then the block has to be in the Fork Choice
// Store!
f.store.nodesLock.RLock()
index, ok := f.store.nodesIndices[root]
if !ok {
f.store.nodesLock.RUnlock()
return false, errUnknownNodeRoot
return false, ErrUnknownNodeRoot
}
node := f.store.nodes[index]
slot := node.slot
@@ -104,28 +102,28 @@ func (s *Store) findSyncedTip(ctx context.Context, node *Node, syncedTips *optim
// SetOptimisticToValid is called with the root of a block that was returned as
// VALID by the EL. This routine recomputes and updates the synced_tips map to
// account for this new tip.
// WARNING: This method returns an error if the root is not found in forkchoice or
// if the root is not a leaf of the fork choice tree.
func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) error {
f.store.nodesLock.RLock()
defer f.store.nodesLock.RUnlock()
// We can only update if given root is in Fork Choice
index, ok := f.store.nodesIndices[root]
if !ok {
return errInvalidNodeIndex
}
// We can only update if root is a leaf in Fork Choice
node := f.store.nodes[index]
if node.bestChild != NonExistentNode {
return errInvalidBestChildIndex
}
f.store.nodesLock.RUnlock()
// Stop early if the root is part of validated tips
f.syncedTips.Lock()
defer f.syncedTips.Unlock()
_, ok = f.syncedTips.validatedTips[root]
if ok {
// Stop early if the node is Valid
optimistic, err := f.IsOptimistic(ctx, root)
if err != nil {
return err
}
if !optimistic {
return nil
}
f.store.nodesLock.RLock()
defer f.store.nodesLock.RUnlock()
// Cache root and slot to validated tips
newTips := make(map[[32]byte]types.Slot)
@@ -206,30 +204,31 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) er
}
// SetOptimisticToInvalid updates the synced_tips map when the block with the given root becomes INVALID.
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte) error {
func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte) ([][32]byte, error) {
f.store.nodesLock.Lock()
defer f.store.nodesLock.Unlock()
invalidRoots := make([][32]byte, 0)
idx, ok := f.store.nodesIndices[root]
if !ok {
return errInvalidNodeIndex
return invalidRoots, errInvalidNodeIndex
}
node := f.store.nodes[idx]
// We only support changing status for the tips in Fork Choice store.
if node.bestChild != NonExistentNode {
return errInvalidNodeIndex
return invalidRoots, errInvalidNodeIndex
}
parentIndex := node.parent
// This should not happen
if parentIndex == NonExistentNode {
return errInvalidNodeIndex
return invalidRoots, errInvalidNodeIndex
}
// Update the weights of the nodes subtracting the INVALID node's weight
weight := node.weight
node = f.store.nodes[parentIndex]
for {
if ctx.Err() != nil {
return ctx.Err()
return invalidRoots, ctx.Err()
}
node.weight -= weight
if node.parent == NonExistentNode {
@@ -242,6 +241,7 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
// delete the invalid node, order is important
f.store.nodes = append(f.store.nodes[:idx], f.store.nodes[idx+1:]...)
delete(f.store.nodesIndices, root)
invalidRoots = append(invalidRoots, root)
// Fix parent and best child for each node
for _, node := range f.store.nodes {
if node.parent == NonExistentNode {
@@ -268,7 +268,7 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
err := f.store.updateBestChildAndDescendant(
parentIndex, uint64(childIndex))
if err != nil {
return err
return invalidRoots, err
}
break
}
@@ -281,24 +281,24 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
parentRoot := parent.root
_, ok = f.syncedTips.validatedTips[parentRoot]
if !ok {
return nil
return invalidRoots, nil
}
leaves, err := f.store.leaves()
if err != nil {
return err
return invalidRoots, err
}
for _, i := range leaves {
node = f.store.nodes[i]
for {
if ctx.Err() != nil {
return ctx.Err()
return invalidRoots, ctx.Err()
}
// Return early if the parent is still a synced tip
if node.root == parentRoot {
return nil
return invalidRoots, nil
}
_, ok = f.syncedTips.validatedTips[node.root]
if ok {
@@ -312,5 +312,5 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root [32]byte)
}
delete(f.syncedTips.validatedTips, parentRoot)
syncedTipsCount.Set(float64(len(f.syncedTips.validatedTips)))
return nil
return invalidRoots, nil
}

View File

@@ -147,10 +147,10 @@ func TestOptimistic(t *testing.T) {
// We test first nodes outside the Fork Choice store
_, err := f.IsOptimistic(ctx, root0)
require.ErrorIs(t, errUnknownNodeRoot, err)
require.ErrorIs(t, ErrUnknownNodeRoot, err)
_, err = f.IsOptimistic(ctx, root1)
require.ErrorIs(t, errUnknownNodeRoot, err)
require.ErrorIs(t, ErrUnknownNodeRoot, err)
// We check all nodes in the Fork Choice store.
op, err := f.IsOptimistic(ctx, nodeA.root)
@@ -220,18 +220,18 @@ func TestSetOptimisticToValid(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
tests := []struct {
root [32]byte // the root of the new VALID block
tips map[[32]byte]types.Slot // the old synced tips
@@ -303,12 +303,55 @@ func TestSetOptimisticToValid(t *testing.T) {
[32]byte{'i'}: 106,
},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'f'}: 105,
[32]byte{'g'}: 104,
[32]byte{'i'}: 106,
[32]byte{'j'}: 102,
},
errInvalidBestChildIndex,
nil,
},
{
[32]byte{'h'},
map[[32]byte]types.Slot{
[32]byte{'z'}: 90,
},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'d'}: 103,
[32]byte{'g'}: 104,
[32]byte{'h'}: 105,
},
nil,
},
{
[32]byte{'h'},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'d'}: 103,
[32]byte{'g'}: 104,
[32]byte{'i'}: 106,
},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'d'}: 103,
[32]byte{'g'}: 104,
[32]byte{'i'}: 106,
},
nil,
},
{
[32]byte{'g'},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'d'}: 103,
[32]byte{'e'}: 104,
},
map[[32]byte]types.Slot{
[32]byte{'b'}: 101,
[32]byte{'e'}: 104,
[32]byte{'g'}: 104,
},
nil,
},
{
[32]byte{'p'},
@@ -356,6 +399,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
newBestChild uint64
newBestDescendant uint64
newParentWeight uint64
returnedRoots [][32]byte
}{
{
[32]byte{'j'},
@@ -368,6 +412,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
3,
4,
8,
[][32]byte{[32]byte{'j'}},
},
{
[32]byte{'j'},
@@ -378,6 +423,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
3,
4,
8,
[][32]byte{[32]byte{'j'}},
},
{
[32]byte{'i'},
@@ -391,6 +437,7 @@ func TestSetOptimisticToInvalid(t *testing.T) {
NonExistentNode,
NonExistentNode,
1,
[][32]byte{[32]byte{'i'}},
},
{
[32]byte{'i'},
@@ -403,24 +450,25 @@ func TestSetOptimisticToInvalid(t *testing.T) {
NonExistentNode,
NonExistentNode,
1,
[][32]byte{[32]byte{'i'}},
},
}
for _, tc := range tests {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
weights := []uint64{10, 10, 9, 7, 1, 6, 2, 3, 1, 1, 1, 0, 0}
f.syncedTips.Lock()
f.syncedTips.validatedTips = tc.tips
@@ -439,8 +487,9 @@ func TestSetOptimisticToInvalid(t *testing.T) {
require.NotEqual(t, NonExistentNode, parentIndex)
parent := f.store.nodes[parentIndex]
f.store.nodesLock.Unlock()
err := f.SetOptimisticToInvalid(context.Background(), tc.root)
roots, err := f.SetOptimisticToInvalid(context.Background(), tc.root)
require.NoError(t, err)
require.DeepEqual(t, tc.returnedRoots, roots)
f.syncedTips.RLock()
_, parentSyncedTip := f.syncedTips.validatedTips[parent.root]
f.syncedTips.RUnlock()
@@ -467,18 +516,18 @@ func TestFindSyncedTip(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
tests := []struct {
root [32]byte // the root of the block
tips map[[32]byte]types.Slot // the synced tips
@@ -553,11 +602,11 @@ func TestFindSyncedTip(t *testing.T) {
func TestIsOptimistic_DeadLock(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 90, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 90, [32]byte{'b'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'c'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'d'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'e'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
tips := map[[32]byte]types.Slot{
[32]byte{'a'}: 100,
[32]byte{'d'}: 102,

View File

@@ -46,6 +46,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -69,6 +70,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -94,6 +96,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -119,6 +122,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
slot,
newRoot,
headRoot,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -185,6 +189,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
honestBlockSlot,
honestBlock,
zeroHash,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -201,6 +206,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
maliciouslyWithheldBlockSlot,
maliciouslyWithheldBlock,
zeroHash,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -249,6 +255,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
honestBlockSlot,
honestBlock,
zeroHash,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -267,6 +274,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
maliciouslyWithheldBlockSlot,
maliciouslyWithheldBlock,
zeroHash,
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -322,6 +330,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
cSlot,
c,
a, // parent
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -345,6 +354,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
bSlot,
b,
a, // parent
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),
@@ -368,6 +378,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) {
dSlot,
d,
b, // parent
params.BeaconConfig().ZeroHash,
jEpoch,
fEpoch,
),

View File

@@ -148,12 +148,12 @@ func (f *ForkChoice) ProposerBoost() [fieldparams.RootLength]byte {
func (f *ForkChoice) InsertOptimisticBlock(
ctx context.Context,
slot types.Slot,
blockRoot, parentRoot [32]byte,
blockRoot, parentRoot, payloadHash [32]byte,
justifiedEpoch, finalizedEpoch types.Epoch) error {
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.InsertOptimisticBlock")
defer span.End()
return f.store.insert(ctx, slot, blockRoot, parentRoot, justifiedEpoch, finalizedEpoch)
return f.store.insert(ctx, slot, blockRoot, parentRoot, payloadHash, justifiedEpoch, finalizedEpoch)
}
// Prune prunes the fork choice store with the new finalized root. The store is only pruned if the input
@@ -342,7 +342,7 @@ func (s *Store) updateCanonicalNodes(ctx context.Context, root [32]byte) error {
// It then updates the new node's parent with best child and descendant node.
func (s *Store) insert(ctx context.Context,
slot types.Slot,
root, parent [32]byte,
root, parent, payloadHash [32]byte,
justifiedEpoch, finalizedEpoch types.Epoch) error {
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
defer span.End()
@@ -371,6 +371,7 @@ func (s *Store) insert(ctx context.Context,
bestChild: NonExistentNode,
bestDescendant: NonExistentNode,
weight: 0,
payloadHash: payloadHash,
}
s.nodesIndices[root] = index

View File

@@ -101,7 +101,7 @@ func TestStore_Head_ContextCancelled(t *testing.T) {
func TestStore_Insert_UnknownParent(t *testing.T) {
// The new node does not have a parent.
s := &Store{nodesIndices: make(map[[32]byte]uint64)}
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, 1, 1))
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, [32]byte{'B'}, params.BeaconConfig().ZeroHash, 1, 1))
assert.Equal(t, 1, len(s.nodes), "Did not insert block")
assert.Equal(t, 1, len(s.nodesIndices), "Did not insert block")
assert.Equal(t, NonExistentNode, s.nodes[0].parent, "Incorrect parent")
@@ -117,13 +117,15 @@ func TestStore_Insert_KnownParent(t *testing.T) {
s.nodes = []*Node{{}}
p := [32]byte{'B'}
s.nodesIndices[p] = 0
require.NoError(t, s.insert(context.Background(), 100, [32]byte{'A'}, p, 1, 1))
payloadHash := [32]byte{'c'}
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")
assert.Equal(t, uint64(0), s.nodes[1].parent, "Incorrect parent")
assert.Equal(t, types.Epoch(1), s.nodes[1].justifiedEpoch, "Incorrect justification")
assert.Equal(t, types.Epoch(1), s.nodes[1].finalizedEpoch, "Incorrect finalization")
assert.Equal(t, [32]byte{'A'}, s.nodes[1].root, "Incorrect root")
assert.Equal(t, payloadHash, s.nodes[1].payloadHash)
}
func TestStore_ApplyScoreChanges_InvalidDeltaLength(t *testing.T) {
@@ -491,18 +493,18 @@ func TestStore_PruneSyncedTips(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
syncedTips := &optimisticStore{
validatedTips: map[[32]byte]types.Slot{
[32]byte{'b'}: 101,

View File

@@ -37,6 +37,7 @@ type Store struct {
type Node struct {
slot types.Slot // slot of the block converted to the node.
root [fieldparams.RootLength]byte // root of the block converted to the node.
payloadHash [fieldparams.RootLength]byte // payloadHash of the block converted to the node.
parent uint64 // parent index of this node.
justifiedEpoch types.Epoch // justifiedEpoch of this node.
finalizedEpoch types.Epoch // finalizedEpoch of this node.

View File

@@ -23,7 +23,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 0
// /
// 2 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(2), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -33,7 +33,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 0
// / \
// head -> 2 1
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(1), params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -63,7 +63,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// head -> 2 1
// |
// 3
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(3), indexToHash(1), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -99,7 +99,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 3
// |
// 4 <- head
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(4), indexToHash(3), params.BeaconConfig().ZeroHash, 1, 1))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -115,7 +115,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4 <- head
// /
// 5 <- justified epoch = 2
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -131,7 +131,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4 <- head
// / \
// 5 6 <- justified epoch = 0
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
// Moved 2 votes to block 5:
// 0
@@ -143,7 +143,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 4
// / \
// 2 votes-> 5 6
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), 1, 1))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(6), indexToHash(4), params.BeaconConfig().ZeroHash, 1, 1))
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(5), 4)
@@ -164,9 +164,9 @@ func TestVotes_CanFindHead(t *testing.T) {
// 8
// |
// 9
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(7), indexToHash(5), params.BeaconConfig().ZeroHash, 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(8), indexToHash(7), params.BeaconConfig().ZeroHash, 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(9), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 1, params.BeaconConfig().ZeroHash, balances, 1)
require.NoError(t, err)
@@ -211,7 +211,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// / \
// 2 votes->9 10
f.ProcessAttestation(context.Background(), []uint64{0, 1}, indexToHash(9), 5)
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(10), indexToHash(8), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
require.NoError(t, err)
@@ -290,7 +290,7 @@ func TestVotes_CanFindHead(t *testing.T) {
// 9 10
// |
// head-> 11
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), 2, 2))
require.NoError(t, f.InsertOptimisticBlock(context.Background(), 0, indexToHash(11), indexToHash(9), params.BeaconConfig().ZeroHash, 2, 2))
r, err = f.Head(context.Background(), 2, indexToHash(5), balances, 2)
require.NoError(t, err)

View File

@@ -25,7 +25,7 @@ func configureTracing(cliCtx *cli.Context) error {
func configureChainConfig(cliCtx *cli.Context) {
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
params.LoadChainConfigFile(chainConfigFileName)
params.LoadChainConfigFile(chainConfigFileName, nil)
}
}
@@ -108,12 +108,12 @@ func configureInteropConfig(cliCtx *cli.Context) {
}
func configureExecutionSetting(cliCtx *cli.Context) error {
if !cliCtx.IsSet(flags.FeeRecipient.Name) {
if !cliCtx.IsSet(flags.SuggestedFeeRecipient.Name) {
return nil
}
c := params.BeaconConfig()
ha := cliCtx.String(flags.FeeRecipient.Name)
ha := cliCtx.String(flags.SuggestedFeeRecipient.Name)
if !common.IsHexAddress(ha) {
return fmt.Errorf("%s is not a valid fee recipient address", ha)
}

View File

@@ -90,19 +90,19 @@ func TestConfigureExecutionSetting(t *testing.T) {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(flags.FeeRecipient.Name, "", "")
require.NoError(t, set.Set(flags.FeeRecipient.Name, "0xB"))
set.String(flags.SuggestedFeeRecipient.Name, "", "")
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xB"))
cliCtx := cli.NewContext(&app, set, nil)
err := configureExecutionSetting(cliCtx)
require.ErrorContains(t, "0xB is not a valid fee recipient address", err)
require.NoError(t, set.Set(flags.FeeRecipient.Name, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
cliCtx = cli.NewContext(&app, set, nil)
err = configureExecutionSetting(cliCtx)
require.NoError(t, err)
assert.Equal(t, common.HexToAddress("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), params.BeaconConfig().DefaultFeeRecipient)
require.NoError(t, set.Set(flags.FeeRecipient.Name, "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
cliCtx = cli.NewContext(&app, set, nil)
err = configureExecutionSetting(cliCtx)
require.NoError(t, err)

View File

@@ -3,6 +3,7 @@ package powchain
import (
"context"
"errors"
"math"
"math/big"
"time"
@@ -24,6 +25,10 @@ var (
// If there are any discrepancies, we must log errors to ensure users can resolve
//the problem and be ready for the merge transition.
func (s *Service) checkTransitionConfiguration(ctx context.Context) {
// If Bellatrix fork epoch is not set, we do not run this check.
if params.BeaconConfig().BellatrixForkEpoch == math.MaxUint64 {
return
}
if s.engineAPIClient == nil {
return
}

View File

@@ -8,11 +8,17 @@ import (
v1 "github.com/prysmaticlabs/prysm/beacon-chain/powchain/engine-api-client/v1"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain/engine-api-client/v1/mocks"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/testing/require"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func Test_checkTransitionConfiguration(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.BellatrixForkEpoch = 0
params.OverrideBeaconConfig(cfg)
ctx := context.Background()
hook := logTest.NewGlobal()

View File

@@ -18,6 +18,7 @@ go_library(
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_golang_jwt_jwt_v4//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)

View File

@@ -17,6 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"go.opencensus.io/trace"
)
const (
@@ -96,6 +97,9 @@ func New(ctx context.Context, endpoint string, opts ...Option) (*Client, error)
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload")
defer span.End()
result := &pb.PayloadStatus{}
err := c.rpc.CallContext(ctx, result, NewPayloadMethod, payload)
if err != nil {
@@ -122,6 +126,9 @@ func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) (
func (c *Client) ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes,
) (*pb.PayloadIDBytes, []byte, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ForkchoiceUpdated")
defer span.End()
result := &ForkchoiceUpdatedResponse{}
err := c.rpc.CallContext(ctx, result, ForkchoiceUpdatedMethod, state, attrs)
if err != nil {
@@ -148,6 +155,9 @@ func (c *Client) ForkchoiceUpdated(
// GetPayload calls the engine_getPayloadV1 method via JSON-RPC.
func (c *Client) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.ExecutionPayload, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetPayload")
defer span.End()
result := &pb.ExecutionPayload{}
err := c.rpc.CallContext(ctx, result, GetPayloadMethod, pb.PayloadIDBytes(payloadId))
return result, handleRPCError(err)
@@ -157,6 +167,9 @@ func (c *Client) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.Executi
func (c *Client) ExchangeTransitionConfiguration(
ctx context.Context, cfg *pb.TransitionConfiguration,
) error {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeTransitionConfiguration")
defer span.End()
// We set terminal block number to 0 as the parameter is not set on the consensus layer.
zeroBigNum := big.NewInt(0)
cfg.TerminalBlockNumber = zeroBigNum.Bytes()
@@ -194,6 +207,9 @@ func (c *Client) ExchangeTransitionConfiguration(
// LatestExecutionBlock fetches the latest execution engine block by calling
// eth_blockByNumber via JSON-RPC.
func (c *Client) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.LatestExecutionBlock")
defer span.End()
result := &pb.ExecutionBlock{}
err := c.rpc.CallContext(
ctx,
@@ -208,6 +224,9 @@ func (c *Client) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock,
// ExecutionBlockByHash fetches an execution engine block by hash by calling
// eth_blockByHash via JSON-RPC.
func (c *Client) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
defer span.End()
result := &pb.ExecutionBlock{}
err := c.rpc.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, false /* no full transaction objects */)
return result, handleRPCError(err)

View File

@@ -596,7 +596,7 @@ func fixtures() map[string]interface{} {
foo := bytesutil.ToBytes32([]byte("foo"))
bar := bytesutil.PadTo([]byte("bar"), 20)
baz := bytesutil.PadTo([]byte("baz"), 256)
baseFeePerGas := big.NewInt(6)
baseFeePerGas := big.NewInt(12345)
executionPayloadFixture := &pb.ExecutionPayload{
ParentHash: foo[:],
FeeRecipient: bar,

View File

@@ -10,7 +10,7 @@ go_library(
"structs_marshalling.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware",
visibility = ["//beacon-chain:__subpackages__"],
visibility = ["//visibility:public"],
deps = [
"//api/gateway/apimiddleware:go_default_library",
"//api/grpc:go_default_library",

View File

@@ -17,6 +17,29 @@ import (
"github.com/prysmaticlabs/prysm/time/slots"
)
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconProposer expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct.
func wrapFeeRecipientsArray(
endpoint *apimiddleware.Endpoint,
_ http.ResponseWriter,
req *http.Request,
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
if _, ok := endpoint.PostRequest.(*feeRecipientsRequestJSON); !ok {
return true, nil
}
recipients := make([]*feeRecipientJson, 0)
if err := json.NewDecoder(req.Body).Decode(&recipients); err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
}
j := &feeRecipientsRequestJSON{Recipients: recipients}
b, err := json.Marshal(j)
if err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
}
req.Body = ioutil.NopCloser(bytes.NewReader(b))
return true, nil
}
// https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array.
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
func wrapAttestationsArray(
@@ -412,6 +435,11 @@ type altairProduceBlockResponseJson struct {
Data *beaconBlockAltairJson `json:"data"`
}
type bellatrixProduceBlockResponseJson struct {
Version string `json:"version"`
Data *beaconBlockBellatrixJson `json:"data"`
}
func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*produceBlockResponseV2Json)
if !ok {
@@ -429,6 +457,11 @@ func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, [
Version: respContainer.Version,
Data: respContainer.Data.AltairBlock,
}
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())) {
actualRespContainer = &bellatrixProduceBlockResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.BellatrixBlock,
}
} else {
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}

View File

@@ -671,7 +671,7 @@ func TestSerializeV2State(t *testing.T) {
})
}
func TestSerializeProduceV2Block(t *testing.T) {
func TestSerializeProducedV2Block(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
response := &produceBlockResponseV2Json{
Version: ethpbv2.Version_PHASE0.String(),
@@ -683,7 +683,6 @@ func TestSerializeProduceV2Block(t *testing.T) {
StateRoot: "root",
Body: &beaconBlockBodyJson{},
},
AltairBlock: nil,
},
}
runDefault, j, errJson := serializeProducedV2Block(response)
@@ -706,7 +705,6 @@ func TestSerializeProduceV2Block(t *testing.T) {
response := &produceBlockResponseV2Json{
Version: ethpbv2.Version_ALTAIR.String(),
Data: &beaconBlockContainerV2Json{
Phase0Block: nil,
AltairBlock: &beaconBlockAltairJson{
Slot: "1",
ProposerIndex: "1",
@@ -732,6 +730,35 @@ func TestSerializeProduceV2Block(t *testing.T) {
require.NotNil(t, beaconBlock.Body)
})
t.Run("Bellatrix", func(t *testing.T) {
response := &produceBlockResponseV2Json{
Version: ethpbv2.Version_BELLATRIX.String(),
Data: &beaconBlockContainerV2Json{
BellatrixBlock: &beaconBlockBellatrixJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &beaconBlockBodyBellatrixJson{},
},
},
}
runDefault, j, errJson := serializeProducedV2Block(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &bellatrixProduceBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data)
beaconBlock := resp.Data
assert.Equal(t, "1", beaconBlock.Slot)
assert.Equal(t, "1", beaconBlock.ProposerIndex)
assert.Equal(t, "root", beaconBlock.ParentRoot)
assert.Equal(t, "root", beaconBlock.StateRoot)
require.NotNil(t, beaconBlock.Body)
})
t.Run("incorrect response type", func(t *testing.T) {
response := &types.Empty{}
runDefault, j, errJson := serializeProducedV2Block(response)

View File

@@ -37,6 +37,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
"/eth/v1/beacon/pool/proposer_slashings",
"/eth/v1/beacon/pool/voluntary_exits",
"/eth/v1/beacon/pool/sync_committees",
"/eth/v1/beacon/weak_subjectivity",
"/eth/v1/node/identity",
"/eth/v1/node/peers",
"/eth/v1/node/peers/{peer_id}",
@@ -47,6 +48,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
"/eth/v1/debug/beacon/states/{state_id}",
"/eth/v2/debug/beacon/states/{state_id}",
"/eth/v1/debug/beacon/heads",
"/eth/v2/debug/beacon/heads",
"/eth/v1/config/fork_schedule",
"/eth/v1/config/deposit_contract",
"/eth/v1/config/spec",
@@ -63,6 +65,7 @@ func (_ *BeaconEndpointFactory) Paths() []string {
"/eth/v1/validator/aggregate_and_proofs",
"/eth/v1/validator/sync_committee_contribution",
"/eth/v1/validator/contribution_and_proofs",
"/eth/v1/validator/prepare_beacon_proposer",
}
}
@@ -141,6 +144,8 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSyncCommitteeSignaturesArray,
}
case "/eth/v1/beacon/weak_subjectivity":
endpoint.GetResponse = &WeakSubjectivityResponse{}
case "/eth/v1/node/identity":
endpoint.GetResponse = &identityResponseJson{}
case "/eth/v1/node/peers":
@@ -168,6 +173,8 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
endpoint.CustomHandlers = []apimiddleware.CustomHandler{handleGetBeaconStateSSZV2}
case "/eth/v1/debug/beacon/heads":
endpoint.GetResponse = &forkChoiceHeadsResponseJson{}
case "/eth/v2/debug/beacon/heads":
endpoint.GetResponse = &v2ForkChoiceHeadsResponseJson{}
case "/eth/v1/config/fork_schedule":
endpoint.GetResponse = &forkScheduleResponseJson{}
case "/eth/v1/config/deposit_contract":
@@ -238,6 +245,11 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapSignedContributionAndProofsArray,
}
case "/eth/v1/validator/prepare_beacon_proposer":
endpoint.PostRequest = &feeRecipientsRequestJSON{}
endpoint.Hooks = apimiddleware.HookCollection{
OnPreDeserializeRequestBodyIntoContainer: wrapFeeRecipientsArray,
}
default:
return nil, errors.New("invalid path")
}

View File

@@ -19,6 +19,20 @@ type genesisResponse_GenesisJson struct {
GenesisForkVersion string `json:"genesis_fork_version" hex:"true"`
}
// WeakSubjectivityResponse is used to marshal/unmarshal the response for the
// /eth/v1/beacon/weak_subjectivity endpoint.
type WeakSubjectivityResponse struct {
Data *struct {
Checkpoint *checkpointJson `json:"ws_checkpoint"`
StateRoot string `json:"state_root" hex:"true"`
} `json:"data"`
}
// feeRecipientsRequestJson is used in /validator/prepare_beacon_proposers API endpoint.
type feeRecipientsRequestJSON struct {
Recipients []*feeRecipientJson `json:"recipients"`
}
// stateRootResponseJson is used in /beacon/states/{state_id}/root API endpoint.
type stateRootResponseJson struct {
Data *stateRootResponse_StateRootJson `json:"data"`
@@ -181,11 +195,16 @@ type beaconStateV2ResponseJson struct {
Data *beaconStateContainerV2Json `json:"data"`
}
// forkChoiceHeadsResponseJson is used in /debug/beacon/heads API endpoint.
// forkChoiceHeadsResponseJson is used in /v1/debug/beacon/heads API endpoint.
type forkChoiceHeadsResponseJson struct {
Data []*forkChoiceHeadJson `json:"data"`
}
// v2ForkChoiceHeadsResponseJson is used in /v2/debug/beacon/heads API endpoint.
type v2ForkChoiceHeadsResponseJson struct {
Data []*v2ForkChoiceHeadJson `json:"data"`
}
// forkScheduleResponseJson is used in /config/fork_schedule API endpoint.
type forkScheduleResponseJson struct {
Data []*forkJson `json:"data"`
@@ -471,6 +490,11 @@ type indexedAttestationJson struct {
Signature string `json:"signature" hex:"true"`
}
type feeRecipientJson struct {
ValidatorIndex uint64 `json:"validator_index"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
}
type attestationJson struct {
AggregationBits string `json:"aggregation_bits" hex:"true"`
Data *attestationDataJson `json:"data"`
@@ -681,6 +705,12 @@ type forkChoiceHeadJson struct {
Slot string `json:"slot"`
}
type v2ForkChoiceHeadJson struct {
Root string `json:"root" hex:"true"`
Slot string `json:"slot"`
ExecutionOptimistic bool `json:"execution_optimistic"`
}
type depositContractJson struct {
ChainId string `json:"chain_id"`
Address string `json:"address"`

View File

@@ -35,6 +35,7 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/v1:go_default_library",
"//beacon-chain/sync:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//crypto/bls:go_default_library",

View File

@@ -5,18 +5,21 @@ import (
"fmt"
"strconv"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
rpchelpers "github.com/prysmaticlabs/prysm/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
"github.com/prysmaticlabs/prysm/proto/migration"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
"github.com/prysmaticlabs/prysm/time/slots"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -40,6 +43,43 @@ func (e *blockIdParseError) Error() string {
return e.message
}
// GetWeakSubjectivity computes the starting epoch of the current weak subjectivity period, and then also
// determines the best block root and state root to use for a Checkpoint Sync starting from that point.
func (bs *Server) GetWeakSubjectivity(ctx context.Context, _ *empty.Empty) (*ethpbv1.WeakSubjectivityResponse, error) {
if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.GenesisTimeFetcher); err != nil {
// This is already a grpc error, so we can't wrap it any further
return nil, err
}
hs, err := bs.HeadFetcher.HeadState(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "could not get head state")
}
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
if err != nil {
return nil, status.Errorf(codes.Internal, "could not get weak subjectivity epoch: %v", err)
}
wsSlot, err := slots.EpochStart(wsEpoch)
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)
if err != nil {
return nil, status.Errorf(codes.Internal, fmt.Sprintf("could not find highest block below slot %d", 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{
Data: &ethpbv1.WeakSubjectivityData{
WsCheckpoint: &ethpbv1.Checkpoint{
Epoch: wsEpoch,
Root: cbr[:],
},
StateRoot: stateRoot[:],
},
}, nil
}
// GetBlockHeader retrieves block header for given block id.
func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest) (*ethpbv1.BlockHeaderResponse, error) {
ctx, span := trace.StartSpan(ctx, "beacon.GetBlockHeader")
@@ -48,14 +88,14 @@ func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest)
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetBlockHeader")
}
v1alpha1Header, err := blk.Header()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block header from block: %v", err)
}
header := migration.V1Alpha1SignedHeaderToV1(v1alpha1Header)
headerRoot, err := header.HashTreeRoot()
headerRoot, err := header.Message.HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not hash block header: %v", err)
}
@@ -118,7 +158,7 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader
return nil, status.Errorf(codes.Internal, "Could not get block header from block: %v", err)
}
header := migration.V1Alpha1SignedHeaderToV1(v1alpha1Header)
headerRoot, err := header.HashTreeRoot()
headerRoot, err := header.Message.HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not hash block header: %v", err)
}
@@ -148,112 +188,59 @@ func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBloc
ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock")
defer span.End()
phase0BlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block)
if ok {
phase0Blk := phase0BlkContainer.Phase0Block
v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(&ethpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: req.Signature})
var wsb block.SignedBeaconBlock
var err error
var v1alpha1Blk interface{}
switch blk := req.Message.(type) {
case *ethpbv2.SignedBeaconBlockContainerV2_Phase0Block:
v1alpha1Blk, err = migration.V1ToV1Alpha1SignedBlock(&ethpbv1.SignedBeaconBlock{Block: blk.Phase0Block, Signature: req.Signature})
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", err)
}
wrappedPhase0Blk := wrapper.WrappedPhase0SignedBeaconBlock(v1alpha1Blk)
root, err := phase0Blk.HashTreeRoot()
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
case *ethpbv2.SignedBeaconBlockContainerV2_AltairBlock:
v1alpha1Blk, err = migration.AltairToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockAltair{Message: blk.AltairBlock, Signature: req.Signature})
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", err)
}
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk},
})
}()
// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
case *ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock:
v1alpha1Blk, err = migration.BellatrixToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockBellatrix{Message: blk.BellatrixBlock, Signature: req.Signature})
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block: %v", err)
}
wsb, err = wrapper.WrappedSignedBeaconBlock(v1alpha1Blk)
default:
return nil, status.Errorf(codes.InvalidArgument, "Unsupported block type %T", req.Message)
}
altairBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock)
if ok {
altairBlk := altairBlkContainer.AltairBlock
v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: req.Signature})
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
wrappedAltairBlk, err := wrapper.WrappedAltairSignedBeaconBlock(v1alpha1Blk)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not prepare Altair block")
}
root, err := altairBlk.HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk},
})
}()
// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedAltairBlk.Proto()); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedAltairBlk, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not wrap beacon block: %v", err)
}
bellatrixBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock)
if ok {
bellatrixBlk := bellatrixBlkContainer.BellatrixBlock
v1alpha1Blk, err := migration.BellatrixToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockBellatrix{Message: bellatrixBlk, Signature: req.Signature})
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
wrappedBellatrixBlk, err := wrapper.WrappedBellatrixSignedBeaconBlock(v1alpha1Blk)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not prepare bellatrix block")
}
root, err := wsb.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
root, err := bellatrixBlk.HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: wsb},
})
}()
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
"Block proposal received via RPC")
bs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedBellatrixBlk},
})
}()
// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wsb.Proto()); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}
// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedBellatrixBlk.Proto()); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}
if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedBellatrixBlk, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}
if err := bs.BlockReceiver.ReceiveBlock(ctx, wsb, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}
return &emptypb.Empty{}, nil
@@ -267,7 +254,7 @@ func (bs *Server) GetBlock(ctx context.Context, req *ethpbv1.BlockRequest) (*eth
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetBlock")
}
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
if err != nil {
@@ -290,7 +277,7 @@ func (bs *Server) GetBlockSSZ(ctx context.Context, req *ethpbv1.BlockRequest) (*
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetBlockSSZ")
}
signedBeaconBlock, err := migration.SignedBeaconBlock(blk)
if err != nil {
@@ -312,7 +299,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetBlockV2")
}
_, err = blk.PbPhase0Block()
@@ -389,7 +376,7 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
blk, err := bs.blockFromBlockID(ctx, req.BlockId)
err = handleGetBlockError(blk, err)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetBlockSSZV2")
}
_, err = blk.PbPhase0Block()

View File

@@ -239,6 +239,21 @@ func TestServer_GetBlockHeader(t *testing.T) {
return
}
expectedBodyRoot, err := tt.want.Block.Body.HashTreeRoot()
require.NoError(t, err)
expectedHeader := &ethpbv1.BeaconBlockHeader{
Slot: tt.want.Block.Slot,
ProposerIndex: tt.want.Block.ProposerIndex,
ParentRoot: tt.want.Block.ParentRoot,
StateRoot: make([]byte, 32),
BodyRoot: expectedBodyRoot[:],
}
expectedHeaderRoot, err := expectedHeader.HashTreeRoot()
require.NoError(t, err)
headerRoot, err := header.Data.Header.Message.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedHeaderRoot, headerRoot)
assert.Equal(t, tt.want.Block.Slot, header.Data.Header.Message.Slot)
assert.DeepEqual(t, tt.want.Block.StateRoot, header.Data.Header.Message.StateRoot)
assert.DeepEqual(t, tt.want.Block.ParentRoot, header.Data.Header.Message.ParentRoot)
@@ -320,6 +335,21 @@ func TestServer_ListBlockHeaders(t *testing.T) {
require.Equal(t, len(tt.want), len(headers.Data))
for i, blk := range tt.want {
expectedBodyRoot, err := blk.Block.Body.HashTreeRoot()
require.NoError(t, err)
expectedHeader := &ethpbv1.BeaconBlockHeader{
Slot: blk.Block.Slot,
ProposerIndex: blk.Block.ProposerIndex,
ParentRoot: blk.Block.ParentRoot,
StateRoot: make([]byte, 32),
BodyRoot: expectedBodyRoot[:],
}
expectedHeaderRoot, err := expectedHeader.HashTreeRoot()
require.NoError(t, err)
headerRoot, err := headers.Data[i].Header.Message.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedHeaderRoot, headerRoot)
assert.Equal(t, blk.Block.Slot, headers.Data[i].Header.Message.Slot)
assert.DeepEqual(t, blk.Block.StateRoot, headers.Data[i].Header.Message.StateRoot)
assert.DeepEqual(t, blk.Block.ParentRoot, headers.Data[i].Header.Message.ParentRoot)

View File

@@ -15,6 +15,7 @@ import (
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
)
// Server defines a server implementation of the gRPC Beacon Chain service,
@@ -34,4 +35,6 @@ type Server struct {
StateFetcher statefetcher.Fetcher
HeadFetcher blockchain.HeadFetcher
V1Alpha1ValidatorServer *v1alpha1validator.Server
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
}

View File

@@ -117,12 +117,7 @@ func (bs *Server) GetFinalityCheckpoints(ctx context.Context, req *ethpb.StateRe
st, err = bs.StateFetcher.State(ctx, req.StateId)
if err != nil {
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
}
return nil, status.Errorf(codes.Internal, "Could not get state: %v", err)
return nil, helpers.PrepareStateFetchGRPCError(err)
}
return &ethpb.StateFinalityCheckpointResponse{

View File

@@ -130,7 +130,7 @@ func (ds *Server) GetBeaconStateSSZV2(ctx context.Context, req *ethpbv2.StateReq
return &ethpbv2.BeaconStateSSZResponseV2{Data: sszState}, nil
}
// ListForkChoiceHeads retrieves the fork choice leaves for the current head.
// ListForkChoiceHeads retrieves the leaves of the current fork choice tree.
func (ds *Server) ListForkChoiceHeads(ctx context.Context, _ *emptypb.Empty) (*ethpbv1.ForkChoiceHeadsResponse, error) {
_, span := trace.StartSpan(ctx, "debug.ListForkChoiceHeads")
defer span.End()
@@ -148,3 +148,27 @@ func (ds *Server) ListForkChoiceHeads(ctx context.Context, _ *emptypb.Empty) (*e
return resp, nil
}
// ListForkChoiceHeadsV2 retrieves the leaves of the current fork choice tree.
func (ds *Server) ListForkChoiceHeadsV2(ctx context.Context, _ *emptypb.Empty) (*ethpbv2.ForkChoiceHeadsResponse, error) {
_, span := trace.StartSpan(ctx, "debug.ListForkChoiceHeadsV2")
defer span.End()
headRoots, headSlots := ds.HeadFetcher.ChainHeads()
resp := &ethpbv2.ForkChoiceHeadsResponse{
Data: make([]*ethpbv2.ForkChoiceHead, len(headRoots)),
}
for i := range headRoots {
isOptimistic, err := ds.HeadFetcher.IsOptimisticForRoot(ctx, headRoots[i])
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if head is optimistic: %v", err)
}
resp.Data[i] = &ethpbv2.ForkChoiceHead{
Root: headRoots[i][:],
Slot: headSlots[i],
ExecutionOptimistic: isOptimistic,
}
}
return resp, nil
}

View File

@@ -61,6 +61,20 @@ func TestGetBeaconStateV2(t *testing.T) {
assert.NotNil(t, resp)
assert.Equal(t, ethpbv2.Version_ALTAIR, resp.Version)
})
t.Run("Bellatrix", func(t *testing.T) {
fakeState, _ := util.DeterministicGenesisStateBellatrix(t, 1)
server := &Server{
StateFetcher: &testutil.MockFetcher{
BeaconState: fakeState,
},
}
resp, err := server.GetBeaconStateV2(context.Background(), &ethpbv2.StateRequestV2{
StateId: make([]byte, 0),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
})
}
func TestGetBeaconStateSSZ(t *testing.T) {
@@ -119,6 +133,24 @@ func TestGetBeaconStateSSZV2(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, sszState, resp.Data)
})
t.Run("Bellatrix", func(t *testing.T) {
fakeState, _ := util.DeterministicGenesisStateBellatrix(t, 1)
sszState, err := fakeState.MarshalSSZ()
require.NoError(t, err)
server := &Server{
StateFetcher: &testutil.MockFetcher{
BeaconState: fakeState,
},
}
resp, err := server.GetBeaconStateSSZV2(context.Background(), &ethpbv2.StateRequestV2{
StateId: make([]byte, 0),
})
require.NoError(t, err)
assert.NotNil(t, resp)
assert.DeepEqual(t, sszState, resp.Data)
})
}
@@ -154,3 +186,56 @@ func TestListForkChoiceHeads(t *testing.T) {
assert.Equal(t, true, found, "Expected head not found")
}
}
func TestListForkChoiceHeadsV2(t *testing.T) {
ctx := context.Background()
expectedSlotsAndRoots := []struct {
Slot types.Slot
Root [32]byte
}{{
Slot: 0,
Root: bytesutil.ToBytes32(bytesutil.PadTo([]byte("foo"), 32)),
}, {
Slot: 1,
Root: bytesutil.ToBytes32(bytesutil.PadTo([]byte("bar"), 32)),
}}
server := &Server{
HeadFetcher: &blockchainmock.ChainService{},
}
resp, err := server.ListForkChoiceHeadsV2(ctx, &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 2, len(resp.Data))
for _, sr := range expectedSlotsAndRoots {
found := false
for _, h := range resp.Data {
if h.Slot == sr.Slot {
found = true
assert.DeepEqual(t, sr.Root[:], h.Root)
}
assert.Equal(t, false, h.ExecutionOptimistic)
}
assert.Equal(t, true, found, "Expected head not found")
}
t.Run("optimistic head", func(t *testing.T) {
server := &Server{
HeadFetcher: &blockchainmock.ChainService{Optimistic: true},
}
resp, err := server.ListForkChoiceHeadsV2(ctx, &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 2, len(resp.Data))
for _, sr := range expectedSlotsAndRoots {
found := false
for _, h := range resp.Data {
if h.Slot == sr.Slot {
found = true
assert.DeepEqual(t, sr.Root[:], h.Root)
}
assert.Equal(t, true, h.ExecutionOptimistic)
}
assert.Equal(t, true, found, "Expected head not found")
}
})
}

View File

@@ -14,6 +14,7 @@ go_library(
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/rpc/statefetcher:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync:go_default_library",
"//config/params:go_default_library",
"//proto/eth/v1:go_default_library",

View File

@@ -1,7 +1,10 @@
package helpers
import (
"errors"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -9,9 +12,13 @@ import (
// PrepareStateFetchGRPCError returns an appropriate gRPC error based on the supplied argument.
// The argument error should be a result of fetching state.
func PrepareStateFetchGRPCError(err error) error {
if errors.Is(err, stategen.ErrSlotBeforeOrigin) {
return status.Errorf(codes.NotFound, "lacking historical data needed to fulfill request")
}
if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok {
return status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr)
} else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
}
if parseErr, ok := err.(*statefetcher.StateIdParseError); ok {
return status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr)
}
return status.Errorf(codes.Internal, "Invalid state ID: %v", err)

View File

@@ -28,7 +28,7 @@ func ValidateSync(ctx context.Context, syncChecker sync.Checker, headFetcher blo
err := grpc.AppendCustomErrorHeader(ctx, syncDetailsContainer)
if err != nil {
return status.Errorf(
codes.InvalidArgument,
codes.Internal,
"Syncing to latest head, not ready to respond. Could not prepare sync details: %v",
err,
)

View File

@@ -30,6 +30,8 @@ go_library(
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
@@ -82,6 +84,7 @@ go_test(
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",

View File

@@ -3,10 +3,13 @@ package validator
import (
"bytes"
"context"
"fmt"
"sort"
"strconv"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
@@ -325,11 +328,29 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
}
// PrepareBeaconProposer --
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
func (vs *Server) PrepareBeaconProposer(
_ context.Context, _ *ethpbv1.PrepareBeaconProposerRequest,
ctx context.Context, request *ethpbv1.PrepareBeaconProposerRequest,
) (*emptypb.Empty, error) {
return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented")
_, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
defer span.End()
var feeRecipients []common.Address
var validatorIndices []types.ValidatorIndex
for _, recipientContainer := range request.Recipients {
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
if !common.IsHexAddress(recipient) {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
}
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
}
if err := vs.V1Alpha1Server.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
}
log.WithFields(log.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses for validator indices")
return &emptypb.Empty{}, nil
}
// ProduceAttestationData requests that the beacon node produces attestation data for

View File

@@ -7,6 +7,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
@@ -2011,3 +2012,65 @@ func TestSubmitContributionAndProofs(t *testing.T) {
require.DeepEqual(t, expectedContributions, savedMsgs)
})
}
func TestPrepareBeaconProposer(t *testing.T) {
type args struct {
request *ethpbv1.PrepareBeaconProposerRequest
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "Happy Path",
args: args{
request: &ethpbv1.PrepareBeaconProposerRequest{
Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{
{
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
ValidatorIndex: 1,
},
},
},
},
wantErr: "",
},
{
name: "invalid fee recipient length",
args: args{
request: &ethpbv1.PrepareBeaconProposerRequest{
Recipients: []*ethpbv1.PrepareBeaconProposerRequest_FeeRecipientContainer{
{
FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength),
ValidatorIndex: 1,
},
},
},
},
wantErr: "Invalid fee recipient address",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
v1Server := &v1alpha1validator.Server{
BeaconDB: db,
}
server := &Server{
V1Alpha1Server: v1Server,
}
_, err := server.PrepareBeaconProposer(ctx, tt.args.request)
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, err)
return
}
require.NoError(t, err)
address, err := server.V1Alpha1Server.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
require.NoError(t, err)
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
})
}
}

View File

@@ -59,7 +59,7 @@ func (bs *Server) ListValidatorAssignments(
if err != nil {
return nil, err
}
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
if err != nil {
msg := fmt.Sprintf("could not replay all blocks from the closest stored state (at slot %d) "+
"to the requested epoch (%d) - %v", startSlot, requestedEpoch, err)

View File

@@ -2,7 +2,6 @@ package beacon
import (
"context"
"fmt"
"strconv"
"github.com/pkg/errors"
@@ -394,46 +393,43 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
return nil, status.Errorf(codes.Internal, "Could not get head block root: %v", err)
}
isGenesis := func(cp *ethpb.Checkpoint) bool {
return bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0
}
// Retrieve genesis block in the event we have genesis checkpoints.
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
if err != nil || genBlock == nil || genBlock.IsNil() || genBlock.Block().IsNil() {
return nil, status.Error(codes.Internal, "Could not get genesis block")
validGenesis := false
validateCP := func(cp *ethpb.Checkpoint, name string) error {
if bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0 {
if validGenesis {
return nil
}
// Retrieve genesis block in the event we have genesis checkpoints.
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
if err != nil || helpers.BeaconBlockIsNil(genBlock) != nil {
return status.Error(codes.Internal, "Could not get genesis block")
}
validGenesis = true
return nil
}
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root))
if err != nil {
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
}
if err := helpers.BeaconBlockIsNil(b); err != nil {
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
}
return nil
}
finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt()
if !isGenesis(finalizedCheckpoint) {
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(finalizedCheckpoint.Root))
if err != nil {
return nil, status.Error(codes.Internal, "Could not get finalized block")
}
if err := helpers.BeaconBlockIsNil(b); err != nil {
return nil, status.Errorf(codes.Internal, "Could not get finalized block: %v", err)
}
if err := validateCP(finalizedCheckpoint, "finalized"); err != nil {
return nil, err
}
justifiedCheckpoint := bs.FinalizationFetcher.CurrentJustifiedCheckpt()
if !isGenesis(justifiedCheckpoint) {
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(justifiedCheckpoint.Root))
if err != nil {
return nil, status.Error(codes.Internal, "Could not get justified block")
}
if err := helpers.BeaconBlockIsNil(b); err != nil {
return nil, status.Errorf(codes.Internal, "Could not get justified block: %v", err)
}
if err := validateCP(justifiedCheckpoint, "justified"); err != nil {
return nil, err
}
prevJustifiedCheckpoint := bs.FinalizationFetcher.PreviousJustifiedCheckpt()
if !isGenesis(prevJustifiedCheckpoint) {
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(prevJustifiedCheckpoint.Root))
if err != nil {
return nil, status.Error(codes.Internal, "Could not get prev justified block")
}
if err := helpers.BeaconBlockIsNil(b); err != nil {
return nil, status.Errorf(codes.Internal, "Could not get prev justified block: %v", err)
}
if err := validateCP(prevJustifiedCheckpoint, "prev justified"); err != nil {
return nil, err
}
fSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch)
@@ -463,40 +459,3 @@ func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, err
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
}, nil
}
// GetWeakSubjectivityCheckpoint retrieves weak subjectivity state root, block root, and epoch.
func (bs *Server) GetWeakSubjectivityCheckpoint(ctx context.Context, _ *emptypb.Empty) (*ethpb.WeakSubjectivityCheckpoint, error) {
hs, err := bs.HeadFetcher.HeadState(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get head state")
}
wsEpoch, err := helpers.LatestWeakSubjectivityEpoch(ctx, hs)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get weak subjectivity epoch")
}
wsSlot, err := slots.EpochStart(wsEpoch)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get weak subjectivity slot")
}
wsState, err := bs.ReplayerBuilder.ForSlot(wsSlot).ReplayBlocks(ctx)
if err != nil {
msg := fmt.Sprintf("error replaying blocks for state at slot %d: %v", wsSlot, err)
return nil, status.Error(codes.Internal, msg)
}
stateRoot, err := wsState.HashTreeRoot(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get weak subjectivity state root")
}
blkRoot, err := wsState.LatestBlockHeader().HashTreeRoot()
if err != nil {
return nil, status.Error(codes.Internal, "Could not get weak subjectivity block root")
}
return &ethpb.WeakSubjectivityCheckpoint{
BlockRoot: blkRoot[:],
StateRoot: stateRoot[:],
Epoch: wsEpoch,
}, nil
}

View File

@@ -366,6 +366,60 @@ func TestServer_ListBlocks_Errors(t *testing.T) {
assert.Equal(t, int32(0), res.TotalSize, "Wanted total size 0")
}
// ensures that if any of the checkpoints are zero-valued, an error will be generated without genesis being present
func TestServer_GetChainHead_NoGenesis(t *testing.T) {
db := dbTest.SetupDB(t)
s, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, s.SetSlot(1))
genBlock := util.NewBeaconBlock()
genBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, fieldparams.RootLength)
require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genBlock)))
gRoot, err := genBlock.Block.HashTreeRoot()
require.NoError(t, err)
cases := []struct {
name string
zeroSetter func(val *ethpb.Checkpoint) error
}{
{
name: "zero-value prev justified",
zeroSetter: s.SetPreviousJustifiedCheckpoint,
},
{
name: "zero-value current justified",
zeroSetter: s.SetCurrentJustifiedCheckpoint,
},
{
name: "zero-value finalized",
zeroSetter: s.SetFinalizedCheckpoint,
},
}
finalized := &ethpb.Checkpoint{Epoch: 1, Root: gRoot[:]}
prevJustified := &ethpb.Checkpoint{Epoch: 2, Root: gRoot[:]}
justified := &ethpb.Checkpoint{Epoch: 3, Root: gRoot[:]}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
require.NoError(t, s.SetPreviousJustifiedCheckpoint(prevJustified))
require.NoError(t, s.SetCurrentJustifiedCheckpoint(justified))
require.NoError(t, s.SetFinalizedCheckpoint(finalized))
require.NoError(t, c.zeroSetter(&ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}))
})
bs := &Server{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wrapper.WrappedPhase0SignedBeaconBlock(genBlock), State: s},
FinalizationFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
}
_, err = bs.GetChainHead(context.Background(), nil)
require.ErrorContains(t, "Could not get genesis block", err)
}
}
func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
db := dbTest.SetupDB(t)

View File

@@ -74,7 +74,7 @@ func (bs *Server) retrieveCommitteesForEpoch(
if err != nil {
return nil, nil, err
}
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
if err != nil {
return nil, nil, status.Errorf(codes.Internal, "error replaying blocks for state at slot %d: %v", startSlot, err)
}

View File

@@ -76,10 +76,9 @@ func TestServer_ListBeaconCommittees_CurrentEpoch(t *testing.T) {
func addDefaultReplayerBuilder(s *Server, h stategen.HistoryAccessor) {
cc := &mockstategen.MockCanonicalChecker{Is: true, Err: nil}
cs := &mockstategen.MockCurrentSlotter{Slot: math.MaxUint64 - 1}
s.ReplayerBuilder = stategen.NewCanonicalBuilder(h, cc, cs)
s.ReplayerBuilder = stategen.NewCanonicalHistory(h, cc, cs)
}
// TODO: test failure
func TestServer_ListBeaconCommittees_PreviousEpoch(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MainnetConfig())

View File

@@ -65,7 +65,7 @@ func (bs *Server) ListValidatorBalances(
if err != nil {
return nil, err
}
requestedState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", startSlot, err))
}
@@ -220,7 +220,7 @@ func (bs *Server) ListValidators(
if err != nil {
return nil, err
}
reqState, err = bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
reqState, err = bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", s, err))
}
@@ -415,7 +415,7 @@ func (bs *Server) GetValidatorActiveSetChanges(
if err != nil {
return nil, err
}
requestedState, err := bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", s, err))
}
@@ -512,7 +512,7 @@ func (bs *Server) GetValidatorParticipation(
}
// ReplayerBuilder ensures that a canonical chain is followed to the slot
beaconState, err := bs.ReplayerBuilder.ForSlot(startSlot).ReplayBlocks(ctx)
beaconState, err := bs.ReplayerBuilder.ReplayerForSlot(startSlot).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", startSlot, err))
}
@@ -831,7 +831,7 @@ func (bs *Server) GetIndividualVotes(
if err != nil {
return nil, err
}
st, err := bs.ReplayerBuilder.ForSlot(s).ReplayBlocks(ctx)
st, err := bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to replay blocks for state at epoch %d: %v", req.Epoch, err)
}

View File

@@ -1505,6 +1505,8 @@ func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T)
func TestServer_GetValidatorParticipation_CurrentAndPrevEpoch(t *testing.T) {
helpers.ClearCache()
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MainnetConfig())
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

View File

@@ -29,7 +29,7 @@ func (ds *Server) GetBeaconState(
)
}
st, err := ds.ReplayerBuilder.ForSlot(q.Slot).ReplayBlocks(ctx)
st, err := ds.ReplayerBuilder.ReplayerForSlot(q.Slot).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", q.Slot, err))
}

View File

@@ -18,14 +18,10 @@ import (
"github.com/prysmaticlabs/prysm/testing/util"
)
func addReplayerBuilder(s *Server, h stategen.HistoryAccessor, is bool, canonErr error, currSlot types.Slot) {
cc := &mockstategen.MockCanonicalChecker{Is: is, Err: canonErr}
cs := &mockstategen.MockCurrentSlotter{Slot: currSlot}
s.ReplayerBuilder = stategen.NewCanonicalBuilder(h, cc, cs)
}
func addDefaultReplayerBuilder(s *Server, h stategen.HistoryAccessor) {
addReplayerBuilder(s, h, true, nil, math.MaxUint64-1)
cc := &mockstategen.MockCanonicalChecker{Is: true}
cs := &mockstategen.MockCurrentSlotter{Slot: math.MaxUint64 - 1}
s.ReplayerBuilder = stategen.NewCanonicalHistory(h, cc, cs)
}
func TestServer_GetBeaconState(t *testing.T) {

View File

@@ -75,6 +75,7 @@ go_library(
"//runtime/version:go_default_library",
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ferranbt_fastssz//:go_default_library",
"@com_github_holiman_uint256//:go_default_library",

View File

@@ -6,8 +6,11 @@ import (
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
emptypb "github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
@@ -116,11 +119,29 @@ func (vs *Server) ProposeBlock(ctx context.Context, rBlk *ethpb.SignedBeaconBloc
return vs.proposeGenericBeaconBlock(ctx, blk)
}
// PrepareBeaconProposer --
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
func (vs *Server) PrepareBeaconProposer(
_ context.Context, _ *ethpb.PrepareBeaconProposerRequest,
ctx context.Context, request *ethpb.PrepareBeaconProposerRequest,
) (*emptypb.Empty, error) {
return &emptypb.Empty{}, status.Error(codes.Unimplemented, "Unimplemented")
_, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
defer span.End()
var feeRecipients []common.Address
var validatorIndices []types.ValidatorIndex
for _, recipientContainer := range request.Recipients {
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
if !common.IsHexAddress(recipient) {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
}
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
}
if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
}
log.WithFields(logrus.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses for validator indices")
return &emptypb.Empty{}, nil
}
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk block.SignedBeaconBlock) (*ethpb.ProposeResponse, error) {

View File

@@ -1,6 +1,7 @@
package validator
import (
"bytes"
"context"
"fmt"
"math/big"
@@ -98,14 +99,19 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
burnAddr := bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength)
switch err == nil {
case true:
feeRecipient = recipient
case errors.As(err, kv.ErrNotFoundFeeRecipient): // If fee recipient is not found, use the default fee recipient.
logrus.WithFields(logrus.Fields{
"validatorIndex": vIdx,
"defaultFeeRecipient": feeRecipient,
}).Error("Fee recipient not found. Using default fee recipient")
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 bytes.Equal(feeRecipient.Bytes(), burnAddr) {
logrus.WithFields(logrus.Fields{
"validatorIndex": vIdx,
"burnAddress": burnAddr,
}).Error("Fee recipient not set. Using burn address")
}
default:
return nil, errors.Wrap(err, "could not get fee recipient in db")
}

View File

@@ -2349,10 +2349,6 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
Graffiti: graffiti[:],
}
proposerIndex := types.ValidatorIndex(40)
addr := common.Address{'a'}
require.NoError(t, proposerServer.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []types.ValidatorIndex{proposerIndex}, []common.Address{addr}))
block, err := proposerServer.GetBeaconBlock(ctx, req)
require.NoError(t, err)
bellatrixBlk, ok := block.GetBlock().(*ethpb.GenericBeaconBlock_Bellatrix)
@@ -2363,8 +2359,18 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
assert.DeepEqual(t, randaoReveal, bellatrixBlk.Bellatrix.Body.RandaoReveal, "Expected block to have correct randao reveal")
assert.DeepEqual(t, req.Graffiti, bellatrixBlk.Bellatrix.Body.Graffiti, "Expected block to have correct Graffiti")
require.LogsDoNotContain(t, hook, "Fee recipient not found. Using default fee recipient.")
require.LogsContain(t, hook, "Fee recipient not set. Using burn address")
require.DeepEqual(t, payload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
// Operator sets default fee recipient to not be burned through beacon node cli.
newHook := logTest.NewGlobal()
params.SetupTestConfigCleanup(t)
cfg = params.MainnetConfig().Copy()
cfg.DefaultFeeRecipient = common.Address{'b'}
params.OverrideBeaconConfig(cfg)
_, err = proposerServer.GetBeaconBlock(ctx, req)
require.NoError(t, err)
require.LogsDoNotContain(t, newHook, "Fee recipient not set. Using burn address")
}
func TestProposer_GetBeaconBlock_Optimistic(t *testing.T) {
@@ -2431,6 +2437,63 @@ func TestProposer_GetSyncAggregate_OK(t *testing.T) {
require.DeepEqual(t, bitfield.NewBitvector512(), aggregate.SyncCommitteeBits)
}
func TestProposer_PrepareBeaconProposer(t *testing.T) {
type args struct {
request *ethpb.PrepareBeaconProposerRequest
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "Happy Path",
args: args{
request: &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
ValidatorIndex: 1,
},
},
},
},
wantErr: "",
},
{
name: "invalid fee recipient length",
args: args{
request: &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{
FeeRecipient: make([]byte, fieldparams.BLSPubkeyLength),
ValidatorIndex: 1,
},
},
},
},
wantErr: "Invalid fee recipient address",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
proposerServer := &Server{BeaconDB: db}
_, err := proposerServer.PrepareBeaconProposer(ctx, tt.args.request)
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, err)
return
}
require.NoError(t, err)
address, err := proposerServer.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
require.NoError(t, err)
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
})
}
}
func majorityVoteBoundaryTime(slot types.Slot) (uint64, uint64) {
slots := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod))
slotStartTime := uint64(mockPOW.GenesisTime) + uint64((slot - (slot % (slots))).Mul(params.BeaconConfig().SecondsPerSlot))

View File

@@ -362,7 +362,7 @@ func (vs *Server) retrieveAfterEpochTransition(ctx context.Context, epoch types.
return nil, err
}
// replay to first slot of following epoch
return vs.ReplayerBuilder.ForSlot(endSlot).ReplayToSlot(ctx, endSlot+1)
return vs.ReplayerBuilder.ReplayerForSlot(endSlot).ReplayToSlot(ctx, endSlot+1)
}
func checkValidatorsAreRecent(headEpoch types.Epoch, req *ethpb.DoppelGangerRequest) (bool, *ethpb.DoppelGangerResponse) {

View File

@@ -178,6 +178,7 @@ func (s *Service) Start() {
stateCache = s.cfg.StateGen.CombinedCache()
}
withCache := stategen.WithCache(stateCache)
ch := stategen.NewCanonicalHistory(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache)
validatorServer := &validatorv1alpha1.Server{
Ctx: s.ctx,
@@ -204,7 +205,7 @@ func (s *Service) Start() {
SlashingsPool: s.cfg.SlashingsPool,
StateGen: s.cfg.StateGen,
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
ReplayerBuilder: ch,
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
BeaconDB: s.cfg.BeaconDB,
}
@@ -221,7 +222,7 @@ func (s *Service) Start() {
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
StateGenService: s.cfg.StateGen,
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
ReplayerBuilder: ch,
},
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
}
@@ -271,9 +272,10 @@ func (s *Service) Start() {
SyncChecker: s.cfg.SyncService,
ReceivedAttestationsBuffer: make(chan *ethpbv1alpha1.Attestation, attestationBufferSize),
CollectedAttestationsBuffer: make(chan []*ethpbv1alpha1.Attestation, attestationBufferSize),
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
ReplayerBuilder: ch,
}
beaconChainServerV1 := &beacon.Server{
CanonicalHistory: ch,
BeaconDB: s.cfg.BeaconDB,
AttestationsPool: s.cfg.AttestationsPool,
SlashingsPool: s.cfg.SlashingsPool,
@@ -289,10 +291,12 @@ func (s *Service) Start() {
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
StateGenService: s.cfg.StateGen,
ReplayerBuilder: ch,
},
HeadFetcher: s.cfg.HeadFetcher,
VoluntaryExitsPool: s.cfg.ExitPool,
V1Alpha1ValidatorServer: validatorServer,
SyncChecker: s.cfg.SyncService,
}
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1)
@@ -314,7 +318,7 @@ func (s *Service) Start() {
HeadFetcher: s.cfg.HeadFetcher,
PeerManager: s.cfg.PeerManager,
PeersFetcher: s.cfg.PeersFetcher,
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
ReplayerBuilder: ch,
}
debugServerV1 := &debug.Server{
BeaconDB: s.cfg.BeaconDB,
@@ -324,7 +328,7 @@ func (s *Service) Start() {
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
StateGenService: s.cfg.StateGen,
ReplayerBuilder: stategen.NewCanonicalBuilder(s.cfg.BeaconDB, s.cfg.ChainInfoFetcher, s.cfg.ChainInfoFetcher, withCache),
ReplayerBuilder: ch,
},
}
ethpbv1alpha1.RegisterDebugServer(s.grpcServer, debugServer)

View File

@@ -11,6 +11,7 @@ go_library(
"//beacon-chain/db:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//config/params:go_default_library",
"//encoding/bytesutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",

View File

@@ -14,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"go.opencensus.io/trace"
)
@@ -110,7 +111,7 @@ func (p *StateProvider) State(ctx context.Context, stateId []byte) (state.Beacon
return nil, errors.Wrap(err, "could not get head state")
}
case "genesis":
s, err = p.BeaconDB.GenesisState(ctx)
s, err = p.StateBySlot(ctx, params.BeaconConfig().GenesisSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get genesis state")
}
@@ -203,7 +204,7 @@ func (p *StateProvider) StateBySlot(ctx context.Context, target types.Slot) (sta
ctx, span := trace.StartSpan(ctx, "statefetcher.StateBySlot")
defer span.End()
st, err := p.ReplayerBuilder.ForSlot(target).ReplayBlocks(ctx)
st, err := p.ReplayerBuilder.ReplayerForSlot(target).ReplayBlocks(ctx)
if err != nil {
msg := fmt.Sprintf("error while replaying history to slot=%d", target)
return nil, errors.Wrap(err, msg)

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