Compare commits

...

18 Commits

Author SHA1 Message Date
Raul Jordan
87357f1226 attempt quic 118 2022-03-15 21:54: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
terence tsao
69a9388515 Spec test v1.1.10 (#10298)
* spectest

* uncomment shas

* Fc spec test service shouldn't not spawn attestation routine

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2022-03-15 13:17:51 +00:00
terence tsao
bd308b6d73 Update default fee recipient error log (#10366) 2022-03-14 20:35:56 -07:00
Radosław Kapka
9abea200a5 Add Bellatrix version to ProduceBlock API (#10361)
* Add Bellatrix version to `ProduceBlock` API

* fix errors

* fix test

* Revert "Auxiliary commit to revert individual files from 8cc6fc9ca3b6ddb67aeafeaddc2a2e1ca6610ded"

This reverts commit 6358ee92d899e8ee6587ad5503cebb40ac92add4.

# Conflicts:
#	beacon-chain/blockchain/optimistic_sync_test.go
#	beacon-chain/blockchain/pow_block_test.go
#	beacon-chain/blockchain/testing/mock_engine.go

* use proper mock

* bzl

* fix build file

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-14 23:08:01 +00:00
terence tsao
730678bf21 Handle pre state Altair with valid payload (#10364)
* Handle pre state Altair with valid payload

* Handle pre state Altair with valid payload
2022-03-14 22:40:08 +00:00
kasey
0b1a777d62 ran gofmt @ v1.17, these are the resulting changes (#10362)
* ran gofmt @ v1.17, these are the resulting changes

* fix some flaky tests that are tripping up this PR

* gofmt

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2022-03-14 15:58:13 -05:00
terence tsao
5461c5b84f Insert stored fee recipient for ForkchoiceUpdated call (#10349)
* Add default

* Add and use fee recipient in db

* Update BUILD.bazel

* Feedback

* Add basic addr check

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-03-14 18:44:40 +00:00
Preston Van Loon
03e7acbf9f increase bazel java max mem to 4gb (#10363)
* increase bazel java max mem to 4gb

* DEBUG: set nocache and modify nogo to force any disk cache misses. Revert this commit after testing in CI

* Revert "DEBUG: set nocache and modify nogo to force any disk cache misses. Revert this commit after testing in CI"

This reverts commit dc41cce43d.
2022-03-14 17:53:42 +00:00
Potuz
6cc88e6454 enable optimistic sync (#10358)
* enable optimistic sync

* revert computation of HTR in notifyNewPayload

* fix tests

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2022-03-14 17:25:40 +00:00
terence tsao
65cea17268 Return early if ttd is not reached (#10359) 2022-03-14 16:38:05 +00:00
Preston Van Loon
aa86c94a91 Update bazel to 5.0.0 (#10352)
* Update to bazel 5

* Add new bazel 5 experimental flags to improve remote caching

* regen cross compile for bazel 5

* gazelle

* remove some old flags

* use heremtic builds sandbox

* minimal downloads for better disk usage

* Restore manual tags for darwin builds
2022-03-14 15:05:08 +00:00
Radosław Kapka
75bb25d515 Bellatrix publish block - API Middleware fixes (#10348)
* Bellatrix publish block - API Middleware fixes

* update field name

* correct docs
2022-03-14 13:36:46 +00:00
terence tsao
1e845bc276 Fix sync committee assignment for Bellatrix (#10356)
* Fix AltairCompatible to account for future state version

* Tests

* gaz

* More tests

* Update comments

* Rename to HigherThanAltairVersionAndEpoch

* HigherEqualThanAltairVersionAndEpoch

Co-authored-by: Nishant Das <nishdas93@gmail.com>
2022-03-14 01:00:33 +00:00
Nishant Das
58f7e942f2 Fix Mnemonic Validation (#10354)
* fix mnemonic validation

* potuz's review

* Add fuzz test for wallet recover

Co-authored-by: prestonvanloon <preston@prysmaticlabs.com>
2022-03-14 07:30:11 +08:00
169 changed files with 2783 additions and 544 deletions

View File

@@ -1 +1 @@
4.2.2
5.0.0

View File

@@ -10,26 +10,26 @@
# Prysm specific remote-cache properties.
#build:remote-cache --disk_cache=
build:remote-cache --remote_download_toplevel
build:remote-cache --remote_download_minimal
build:remote-cache --remote_cache=grpc://bazel-remote-cache:9092
build:remote-cache --experimental_remote_downloader=grpc://bazel-remote-cache:9092
build:remote-cache --remote_local_fallback
build:remote-cache --experimental_remote_cache_async
build:remote-cache --experimental_remote_merkle_tree_cache
build:remote-cache --experimental_action_cache_store_output_metadata
build:remote-cache --experimental_remote_cache_compression
# Enforce stricter environment rules, which eliminates some non-hermetic
# behavior and therefore improves both the remote cache hit rate and the
# correctness and repeatability of the build.
build:remote-cache --incompatible_strict_action_env=true
build --experimental_use_hermetic_linux_sandbox
# Import workspace options.
import %workspace%/.bazelrc
startup --host_jvm_args=-Xmx2g --host_jvm_args=-Xms2g
query --repository_cache=/tmp/repositorycache
query --experimental_repository_cache_hardlinks
build --repository_cache=/tmp/repositorycache
build --experimental_repository_cache_hardlinks
startup --host_jvm_args=-Xmx4g --host_jvm_args=-Xms2g
build --experimental_strict_action_env
build --disk_cache=/tmp/bazelbuilds
build --experimental_multi_threaded_digest
build --sandbox_tmpfs_path=/tmp
build --verbose_failures
build --announce_rc

View File

@@ -3,7 +3,7 @@ load("@com_github_atlassian_bazel_tools//gometalinter:def.bzl", "gometalinter")
load("@com_github_atlassian_bazel_tools//goimports:def.bzl", "goimports")
load("@io_kubernetes_build//defs:run_in_workspace.bzl", "workspace_binary")
load("@io_bazel_rules_go//go:def.bzl", "nogo")
load("@graknlabs_bazel_distribution//common:rules.bzl", "assemble_targz", "assemble_versioned")
load("@vaticle_bazel_distribution//common:rules.bzl", "assemble_targz", "assemble_versioned")
load("@bazel_skylib//rules:common_settings.bzl", "string_setting")
prefix = "github.com/prysmaticlabs/prysm"

View File

@@ -117,13 +117,6 @@ http_archive(
urls = ["https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.76/fuzzit_Linux_x86_64.zip"],
)
git_repository(
name = "graknlabs_bazel_distribution",
commit = "962f3a7e56942430c0ec120c24f9e9f2a9c2ce1a",
remote = "https://github.com/graknlabs/bazel-distribution",
shallow_since = "1569509514 +0300",
)
load(
"@io_bazel_rules_docker//repositories:repositories.bzl",
container_repositories = "repositories",
@@ -222,7 +215,7 @@ filegroup(
url = "https://github.com/eth-clients/slashing-protection-interchange-tests/archive/b8413ca42dc92308019d0d4db52c87e9e125c4e9.tar.gz",
)
consensus_spec_version = "v1.1.9"
consensus_spec_version = "v1.1.10"
bls_test_version = "v0.1.1"
@@ -238,7 +231,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "207d9c326ba4fa1f34bab7b6169201c32f2611755db030909a3405873445e0ba",
sha256 = "28043009cc2f6fc9804e73c8c1fc2cb27062f1591e6884f3015ae1dd7a276883",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
)
@@ -254,7 +247,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "a3995b39f412db236b2f1db909f288218da53cb53b9923b71dda9d144d68f40a",
sha256 = "bc1a283ca068f310f04d70c4f6a8eaa0b8f7e9318073a8bdc2ee233111b4e339",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
)
@@ -270,7 +263,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "76cea7a4c8e32d458ad456b54bfbb30bc772481a91954a4cd97e229aa3023b1d",
sha256 = "bbabb482c229ff9d4e2c7b77c992edb452f9d0af7c6d8dd4f922f06a7b101e81",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
)
@@ -285,7 +278,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "0fc429684775f943250dce1f9c485ac25e26c6395d7f585c8d1317becec2ace7",
sha256 = "408a5524548ad3fcf387f65ac7ec52781d9ee899499720bb12451b48a15818d4",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)

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

@@ -16,10 +16,13 @@ func TestHeadSlot_DataRace(t *testing.T) {
s := &Service{
cfg: &config{BeaconDB: beaconDB},
}
wait := make(chan struct{})
go func() {
defer close(wait)
require.NoError(t, s.saveHead(context.Background(), [32]byte{}))
}()
s.HeadSlot()
<-wait
}
func TestHeadRoot_DataRace(t *testing.T) {
@@ -28,11 +31,14 @@ func TestHeadRoot_DataRace(t *testing.T) {
cfg: &config{BeaconDB: beaconDB, StateGen: stategen.New(beaconDB)},
head: &head{root: [32]byte{'A'}},
}
wait := make(chan struct{})
go func() {
defer close(wait)
require.NoError(t, s.saveHead(context.Background(), [32]byte{}))
}()
_, err := s.HeadRoot(context.Background())
require.NoError(t, err)
<-wait
}
func TestHeadBlock_DataRace(t *testing.T) {
@@ -41,11 +47,14 @@ func TestHeadBlock_DataRace(t *testing.T) {
cfg: &config{BeaconDB: beaconDB, StateGen: stategen.New(beaconDB)},
head: &head{block: wrapper.WrappedPhase0SignedBeaconBlock(&ethpb.SignedBeaconBlock{})},
}
wait := make(chan struct{})
go func() {
defer close(wait)
require.NoError(t, s.saveHead(context.Background(), [32]byte{}))
}()
_, err := s.HeadBlock(context.Background())
require.NoError(t, err)
<-wait
}
func TestHeadState_DataRace(t *testing.T) {
@@ -53,9 +62,12 @@ func TestHeadState_DataRace(t *testing.T) {
s := &Service{
cfg: &config{BeaconDB: beaconDB, StateGen: stategen.New(beaconDB)},
}
wait := make(chan struct{})
go func() {
defer close(wait)
require.NoError(t, s.saveHead(context.Background(), [32]byte{}))
}()
_, err := s.HeadState(context.Background())
require.NoError(t, err)
<-wait
}

View File

@@ -437,3 +437,79 @@ func TestService_IsOptimisticForRoot_DoublyLinkedTree(t *testing.T) {
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

@@ -1,3 +1,4 @@
//go:build !develop
// +build !develop
package blockchain

View File

@@ -9,4 +9,8 @@ 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")
)

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

@@ -76,6 +76,9 @@ 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 {
return nil, errors.Wrap(err, "could not set block to valid")
}
return payloadID, nil
}
@@ -119,7 +122,9 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int, hea
// During the transition event, the transition block should be verified for sanity.
if isPreBellatrix(preStateVersion) {
return nil
// Handle case where pre-state is Altair but block contains payload.
// To reach here, the block must have contained a valid payload.
return s.validateMergeBlock(ctx, blk)
}
atTransition, err := blocks.IsMergeTransitionBlockUsingPayloadHeader(header, body)
if err != nil {
@@ -140,14 +145,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

@@ -36,7 +36,6 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
require.NoError(t, err)
require.NoError(t, beaconDB.SaveBlock(ctx, altairBlk))
require.NoError(t, beaconDB.SaveBlock(ctx, bellatrixBlk))
fcs := protoarray.New(0, 0, [32]byte{'a'})
opts := []Option{
WithDatabase(beaconDB),
@@ -45,6 +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))
tests := []struct {
name string
@@ -188,6 +188,13 @@ func Test_NotifyNewPayload(t *testing.T) {
},
},
}
a := &ethpb.SignedBeaconBlockAltair{
Block: &ethpb.BeaconBlockAltair{
Body: &ethpb.BeaconBlockBodyAltair{},
},
}
altairBlk, err := wrapper.WrappedSignedBeaconBlock(a)
require.NoError(t, err)
bellatrixBlk, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
service, err := NewService(ctx, opts...)
@@ -238,10 +245,29 @@ func Test_NotifyNewPayload(t *testing.T) {
errString: "could not validate execution payload from execution engine: payload status is INVALID",
},
{
name: "altair pre state",
name: "altair pre state, altair block",
postState: bellatrixState,
preState: altairState,
blk: bellatrixBlk,
blk: altairBlk,
},
{
name: "altair pre state, happy case",
postState: bellatrixState,
preState: altairState,
blk: func() block.SignedBeaconBlock {
blk := &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
},
},
},
}
b, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
return b
}(),
},
{
name: "could not get merge block",
@@ -304,27 +330,29 @@ func Test_NotifyNewPayload(t *testing.T) {
},
}
for _, tt := range tests {
engine := &mockEngineService{newPayloadError: tt.newPayloadErr, 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
var payload *ethpb.ExecutionPayloadHeader
if tt.preState.Version() == version.Bellatrix {
payload, err = tt.preState.LatestExecutionPayloadHeader()
require.NoError(t, err)
}
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
require.NoError(t, err)
}
t.Run(tt.name, func(t *testing.T) {
engine := &mockEngineService{newPayloadError: tt.newPayloadErr, 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
var payload *ethpb.ExecutionPayloadHeader
if tt.preState.Version() == version.Bellatrix {
payload, err = tt.preState.LatestExecutionPayloadHeader()
require.NoError(t, err)
}
err := service.notifyNewPayload(ctx, tt.preState.Version(), payload, tt.postState, tt.blk)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
require.NoError(t, err)
}
})
}
}
@@ -347,6 +375,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
@@ -358,6 +392,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
@@ -365,6 +400,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
@@ -376,6 +412,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
@@ -383,6 +420,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
@@ -394,6 +432,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
@@ -401,6 +440,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
@@ -412,6 +452,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
@@ -419,6 +460,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)
@@ -443,8 +485,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: make([]byte, 32),
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

@@ -111,7 +111,6 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
return errors.Wrap(err, "could not verify new payload")
}
// TODO(10261) Check optimistic status
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
return err
}

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

@@ -127,7 +127,7 @@ func (s *Service) Start() {
saved := s.cfg.FinalizedStateAtStartUp
if saved != nil && !saved.IsNil() {
if err := s.startFromSavedState(saved); err != nil {
if err := s.StartFromSavedState(saved); err != nil {
log.Fatal(err)
}
} else {
@@ -164,7 +164,7 @@ func (s *Service) Status() error {
return nil
}
func (s *Service) startFromSavedState(saved state.BeaconState) error {
func (s *Service) StartFromSavedState(saved state.BeaconState) error {
log.Info("Blockchain data already exists in DB, initializing...")
s.genesisTime = time.Unix(int64(saved.GenesisTime()), 0) // lint:ignore uintcast -- Genesis time will not exceed int64 in your lifetime.
s.cfg.AttService.SetGenesisTime(saved.GenesisTime())

View File

@@ -290,7 +290,7 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
require.NoError(t, err)
require.NoError(t, c.startFromSavedState(headState))
require.NoError(t, c.StartFromSavedState(headState))
headBlk, err := c.HeadBlock(ctx)
require.NoError(t, err)
assert.DeepEqual(t, headBlock, headBlk.Proto(), "Head block incorrect")
@@ -333,7 +333,7 @@ func TestChainService_InitializeChainInfo_SetHeadAtGenesis(t *testing.T) {
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}))
require.NoError(t, err)
require.NoError(t, c.startFromSavedState(headState))
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")
@@ -393,7 +393,7 @@ func TestChainService_InitializeChainInfo_HeadSync(t *testing.T) {
require.NoError(t, err)
c, err := NewService(ctx, WithDatabase(beaconDB), WithStateGen(stategen.New(beaconDB)), WithAttestationService(attSrv), WithStateNotifier(&mock.MockStateNotifier{}), WithFinalizedStateAtStartUp(headState))
require.NoError(t, err)
require.NoError(t, c.startFromSavedState(headState))
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")

View File

@@ -1,3 +1,4 @@
//go:build fuzz
// +build fuzz
package cache

View File

@@ -1,3 +1,4 @@
//go:build fuzz
// +build fuzz
// This file is used in fuzzer builds to bypass global committee caches.

View File

@@ -1,3 +1,4 @@
//go:build !fuzz
// +build !fuzz
package cache

View File

@@ -1,3 +1,4 @@
//go:build fuzz
// +build fuzz
// This file is used in fuzzer builds to bypass proposer indices caches.

View File

@@ -1,3 +1,4 @@
//go:build !fuzz
// +build !fuzz
package cache

View File

@@ -1,3 +1,4 @@
//go:build fuzz
// +build fuzz
package cache

View File

@@ -17,13 +17,15 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["slot_epoch_test.go"],
embed = [":go_default_library"],
deps = [
":go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/v1:go_default_library",
"//config/params:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
],

View File

@@ -46,9 +46,9 @@ func NextEpoch(state state.ReadOnlyBeaconState) types.Epoch {
return slots.ToEpoch(state.Slot()) + 1
}
// AltairCompatible returns if the input state `s` is altair compatible and input epoch `e` is higher equal than fork epoch.
func AltairCompatible(s state.BeaconState, e types.Epoch) bool {
return s.Version() == version.Altair && e >= params.BeaconConfig().AltairForkEpoch
// HigherEqualThanAltairVersionAndEpoch returns if the input state `s` has a higher version number than Altair state and input epoch `e` is higher equal than fork epoch.
func HigherEqualThanAltairVersionAndEpoch(s state.BeaconState, e types.Epoch) bool {
return s.Version() >= version.Altair && e >= params.BeaconConfig().AltairForkEpoch
}
// CanUpgradeToAltair returns true if the input `slot` can upgrade to Altair.

View File

@@ -1,14 +1,17 @@
package time
package time_test
import (
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
"github.com/prysmaticlabs/prysm/config/params"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"github.com/prysmaticlabs/prysm/time/slots"
)
@@ -42,7 +45,7 @@ func TestCurrentEpoch_OK(t *testing.T) {
for _, tt := range tests {
state, err := v1.InitializeFromProto(&eth.BeaconState{Slot: tt.slot})
require.NoError(t, err)
assert.Equal(t, tt.epoch, CurrentEpoch(state), "ActiveCurrentEpoch(%d)", state.Slot())
assert.Equal(t, tt.epoch, time.CurrentEpoch(state), "ActiveCurrentEpoch(%d)", state.Slot())
}
}
@@ -58,7 +61,7 @@ func TestPrevEpoch_OK(t *testing.T) {
for _, tt := range tests {
state, err := v1.InitializeFromProto(&eth.BeaconState{Slot: tt.slot})
require.NoError(t, err)
assert.Equal(t, tt.epoch, PrevEpoch(state), "ActivePrevEpoch(%d)", state.Slot())
assert.Equal(t, tt.epoch, time.PrevEpoch(state), "ActivePrevEpoch(%d)", state.Slot())
}
}
@@ -76,7 +79,7 @@ func TestNextEpoch_OK(t *testing.T) {
for _, tt := range tests {
state, err := v1.InitializeFromProto(&eth.BeaconState{Slot: tt.slot})
require.NoError(t, err)
assert.Equal(t, tt.epoch, NextEpoch(state), "NextEpoch(%d)", state.Slot())
assert.Equal(t, tt.epoch, time.NextEpoch(state), "NextEpoch(%d)", state.Slot())
}
}
@@ -108,7 +111,7 @@ func TestCanUpgradeToAltair(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := CanUpgradeToAltair(tt.slot); got != tt.want {
if got := time.CanUpgradeToAltair(tt.slot); got != tt.want {
t.Errorf("canUpgradeToAltair() = %v, want %v", got, tt.want)
}
})
@@ -143,7 +146,7 @@ func TestCanUpgradeBellatrix(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := CanUpgradeToBellatrix(tt.slot); got != tt.want {
if got := time.CanUpgradeToBellatrix(tt.slot); got != tt.want {
t.Errorf("CanUpgradeToBellatrix() = %v, want %v", got, tt.want)
}
})
@@ -178,6 +181,85 @@ func TestCanProcessEpoch_TrueOnEpochsLastSlot(t *testing.T) {
b := &eth.BeaconState{Slot: tt.slot}
s, err := v1.InitializeFromProto(b)
require.NoError(t, err)
assert.Equal(t, tt.canProcessEpoch, CanProcessEpoch(s), "CanProcessEpoch(%d)", tt.slot)
assert.Equal(t, tt.canProcessEpoch, time.CanProcessEpoch(s), "CanProcessEpoch(%d)", tt.slot)
}
}
func TestAltairCompatible(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.AltairForkEpoch = 1
cfg.BellatrixForkEpoch = 2
params.OverrideBeaconConfig(cfg)
type args struct {
s state.BeaconState
e types.Epoch
}
tests := []struct {
name string
args args
want bool
}{
{
name: "phase0 state",
args: args{
s: func() state.BeaconState {
st, _ := util.DeterministicGenesisState(t, 1)
return st
}(),
},
want: false,
},
{
name: "altair state, altair epoch",
args: args{
s: func() state.BeaconState {
st, _ := util.DeterministicGenesisStateAltair(t, 1)
return st
}(),
e: params.BeaconConfig().AltairForkEpoch,
},
want: true,
},
{
name: "bellatrix state, bellatrix epoch",
args: args{
s: func() state.BeaconState {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
return st
}(),
e: params.BeaconConfig().BellatrixForkEpoch,
},
want: true,
},
{
name: "bellatrix state, altair epoch",
args: args{
s: func() state.BeaconState {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
return st
}(),
e: params.BeaconConfig().AltairForkEpoch,
},
want: true,
},
{
name: "bellatrix state, phase0 epoch",
args: args{
s: func() state.BeaconState {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
return st
}(),
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := time.HigherEqualThanAltairVersionAndEpoch(tt.args.s, tt.args.e); got != tt.want {
t.Errorf("HigherEqualThanAltairVersionAndEpoch() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -45,12 +45,13 @@ 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.
PowchainData(ctx context.Context) (*ethpb.ETH1ChainData, error)
// Fee reicipients operations.
FeeRecipientByValidatorID(ctx context.Context, id uint64) (common.Address, error)
FeeRecipientByValidatorID(ctx context.Context, id types.ValidatorIndex) (common.Address, error)
// origin checkpoint sync support
OriginBlockRoot(ctx context.Context) ([32]byte, error)
}
@@ -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.
@@ -81,7 +83,7 @@ type NoHeadAccessDatabase interface {
// Run any required database migrations.
RunMigrations(ctx context.Context) error
// Fee reicipients operations.
SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []uint64, addrs []common.Address) error
SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []types.ValidatorIndex, addrs []common.Address) error
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint types.Slot) error
}

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",
@@ -91,6 +92,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

@@ -395,13 +395,13 @@ func (s *Store) HighestSlotBlocksBelow(ctx context.Context, slot types.Slot) ([]
// FeeRecipientByValidatorID returns the fee recipient for a validator id.
// `ErrNotFoundFeeRecipient` is returned if the validator id is not found.
func (s *Store) FeeRecipientByValidatorID(ctx context.Context, id uint64) (common.Address, error) {
func (s *Store) FeeRecipientByValidatorID(ctx context.Context, id types.ValidatorIndex) (common.Address, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.FeeRecipientByValidatorID")
defer span.End()
var addr []byte
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(feeRecipientBucket)
addr = bkt.Get(bytesutil.Uint64ToBytesBigEndian(id))
addr = bkt.Get(bytesutil.Uint64ToBytesBigEndian(uint64(id)))
if addr == nil {
return errors.Wrapf(ErrNotFoundFeeRecipient, "validator id %d", id)
}
@@ -412,7 +412,7 @@ func (s *Store) FeeRecipientByValidatorID(ctx context.Context, id uint64) (commo
// SaveFeeRecipientsByValidatorIDs saves the fee recipients for validator ids.
// Error is returned if `ids` and `recipients` are not the same length.
func (s *Store) SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []uint64, feeRecipients []common.Address) error {
func (s *Store) SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []types.ValidatorIndex, feeRecipients []common.Address) error {
_, span := trace.StartSpan(ctx, "BeaconDB.SaveFeeRecipientByValidatorID")
defer span.End()
@@ -423,7 +423,7 @@ func (s *Store) SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []uint6
return s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(feeRecipientBucket)
for i, id := range ids {
if err := bkt.Put(bytesutil.Uint64ToBytesBigEndian(id), feeRecipients[i].Bytes()); err != nil {
if err := bkt.Put(bytesutil.Uint64ToBytesBigEndian(uint64(id)), feeRecipients[i].Bytes()); err != nil {
return err
}
}

View File

@@ -595,11 +595,11 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
func TestStore_FeeRecipientByValidatorID(t *testing.T) {
db := setupDB(t)
ctx := context.Background()
ids := []uint64{0, 0, 0}
ids := []types.ValidatorIndex{0, 0, 0}
feeRecipients := []common.Address{{}, {}, {}, {}}
require.ErrorContains(t, "validatorIDs and feeRecipients must be the same length", db.SaveFeeRecipientsByValidatorIDs(ctx, ids, feeRecipients))
ids = []uint64{0, 1, 2}
ids = []types.ValidatorIndex{0, 1, 2}
feeRecipients = []common.Address{{'a'}, {'b'}, {'c'}}
require.NoError(t, db.SaveFeeRecipientsByValidatorIDs(ctx, ids, feeRecipients))
f, err := db.FeeRecipientByValidatorID(ctx, 0)

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

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

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

View File

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

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

@@ -12,7 +12,7 @@ func (s *Store) removeNode(ctx context.Context, root [32]byte) error {
node, ok := s.nodeByRoot[root]
if !ok || node == nil {
return errNilNode
return ErrNilNode
}
if !node.optimistic || node.parent == nil {
return errInvalidOptimisticStatus

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

@@ -40,7 +40,7 @@ func (f *ForkChoice) IsOptimistic(ctx context.Context, root [32]byte) (bool, err
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

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)

View File

@@ -1,6 +1,8 @@
package node
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/cmd"
@@ -105,10 +107,17 @@ func configureInteropConfig(cliCtx *cli.Context) {
}
}
func configureExecutionSetting(cliCtx *cli.Context) {
if cliCtx.IsSet(flags.FeeRecipient.Name) {
c := params.BeaconConfig()
c.FeeRecipient = common.HexToAddress(cliCtx.String(flags.FeeRecipient.Name))
params.OverrideBeaconConfig(c)
func configureExecutionSetting(cliCtx *cli.Context) error {
if !cliCtx.IsSet(flags.FeeRecipient.Name) {
return nil
}
c := params.BeaconConfig()
ha := cliCtx.String(flags.FeeRecipient.Name)
if !common.IsHexAddress(ha) {
return fmt.Errorf("%s is not a valid fee recipient address", ha)
}
c.DefaultFeeRecipient = common.HexToAddress(ha)
params.OverrideBeaconConfig(c)
return nil
}

View File

@@ -93,9 +93,20 @@ func TestConfigureExecutionSetting(t *testing.T) {
set.String(flags.FeeRecipient.Name, "", "")
require.NoError(t, set.Set(flags.FeeRecipient.Name, "0xB"))
cliCtx := cli.NewContext(&app, set, nil)
err := configureExecutionSetting(cliCtx)
require.ErrorContains(t, "0xB is not a valid fee recipient address", err)
configureExecutionSetting(cliCtx)
assert.Equal(t, common.HexToAddress("0xB"), params.BeaconConfig().FeeRecipient)
require.NoError(t, set.Set(flags.FeeRecipient.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"))
cliCtx = cli.NewContext(&app, set, nil)
err = configureExecutionSetting(cliCtx)
require.NoError(t, err)
assert.Equal(t, common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), params.BeaconConfig().DefaultFeeRecipient)
}
func TestConfigureNetwork(t *testing.T) {

View File

@@ -120,7 +120,9 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
configureEth1Config(cliCtx)
configureNetwork(cliCtx)
configureInteropConfig(cliCtx)
configureExecutionSetting(cliCtx)
if err := configureExecutionSetting(cliCtx); err != nil {
return nil, err
}
// Initializes any forks here.
params.BeaconConfig().InitializeForkSchedule()

View File

@@ -181,9 +181,14 @@ type altairPublishBlockRequestJson struct {
Signature string `json:"signature" hex:"true"`
}
type bellatrixPublishBlockRequestJson struct {
BellatrixBlock *beaconBlockBellatrixJson `json:"bellatrix_block"`
Signature string `json:"signature" hex:"true"`
}
// setInitialPublishBlockPostRequest is triggered before we deserialize the request JSON into a struct.
// We don't know which version of the block got posted, but we can determine it from the slot.
// We know that both Phase 0 and Altair blocks have a Message field with a Slot field,
// We know that blocks of all versions have a Message field with a Slot field,
// so we deserialize the request into a struct s, which has the right fields, to obtain the slot.
// Once we know the slot, we can determine what the PostRequest field of the endpoint should be, and we set it appropriately.
func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint,
@@ -207,17 +212,20 @@ func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint,
if err != nil {
return false, apimiddleware.InternalServerErrorWithMessage(err, "slot is not an unsigned integer")
}
if slots.ToEpoch(types.Slot(slot)) < params.BeaconConfig().AltairForkEpoch {
currentEpoch := slots.ToEpoch(types.Slot(slot))
if currentEpoch < params.BeaconConfig().AltairForkEpoch {
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
} else {
} else if currentEpoch < params.BeaconConfig().BellatrixForkEpoch {
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
} else {
endpoint.PostRequest = &signedBeaconBlockBellatrixContainerJson{}
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
return true, nil
}
// In preparePublishedBlock we transform the PostRequest.
// gRPC expects either a phase0_block or an altair_block field in the JSON object, but we have a message field at this point.
// gRPC expects an XXX_block field in the JSON object, but we have a message field at this point.
// We do a simple conversion depending on the type of endpoint.PostRequest
// (which was filled out previously in setInitialPublishBlockPostRequest).
func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
@@ -239,6 +247,15 @@ func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWrit
endpoint.PostRequest = actualPostReq
return nil
}
if block, ok := endpoint.PostRequest.(*signedBeaconBlockBellatrixContainerJson); ok {
// Prepare post request that can be properly decoded on gRPC side.
actualPostReq := &bellatrixPublishBlockRequestJson{
BellatrixBlock: block.Message,
Signature: block.Signature,
}
endpoint.PostRequest = actualPostReq
return nil
}
return apimiddleware.InternalServerError(errors.New("unsupported block type"))
}

View File

@@ -363,6 +363,11 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) {
}
func TestSetInitialPublishBlockPostRequest(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.BellatrixForkEpoch = params.BeaconConfig().AltairForkEpoch + 1
params.OverrideBeaconConfig(cfg)
endpoint := &apimiddleware.Endpoint{}
s := struct {
Message struct {
@@ -397,6 +402,21 @@ func TestSetInitialPublishBlockPostRequest(t *testing.T) {
assert.Equal(t, apimiddleware.RunDefault(true), runDefault)
assert.Equal(t, reflect.TypeOf(signedBeaconBlockAltairContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name())
})
t.Run("Bellatrix", func(t *testing.T) {
slot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
require.NoError(t, err)
s.Message = struct{ Slot string }{Slot: strconv.FormatUint(uint64(slot), 10)}
j, err := json.Marshal(s)
require.NoError(t, err)
var body bytes.Buffer
_, err = body.Write(j)
require.NoError(t, err)
request := httptest.NewRequest("POST", "http://foo.example", &body)
runDefault, errJson := setInitialPublishBlockPostRequest(endpoint, nil, request)
require.Equal(t, true, errJson == nil)
assert.Equal(t, apimiddleware.RunDefault(true), runDefault)
assert.Equal(t, reflect.TypeOf(signedBeaconBlockBellatrixContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name())
})
}
func TestPreparePublishedBlock(t *testing.T) {
@@ -428,6 +448,20 @@ func TestPreparePublishedBlock(t *testing.T) {
assert.Equal(t, true, ok)
})
t.Run("Bellatrix", func(t *testing.T) {
endpoint := &apimiddleware.Endpoint{
PostRequest: &signedBeaconBlockBellatrixContainerJson{
Message: &beaconBlockBellatrixJson{
Body: &beaconBlockBodyBellatrixJson{},
},
},
}
errJson := preparePublishedBlock(endpoint, nil, nil)
require.Equal(t, true, errJson == nil)
_, ok := endpoint.PostRequest.(*bellatrixPublishBlockRequestJson)
assert.Equal(t, true, ok)
})
t.Run("unsupported block type", func(t *testing.T) {
errJson := preparePublishedBlock(&apimiddleware.Endpoint{}, nil, nil)
assert.Equal(t, true, strings.Contains(errJson.Msg(), "unsupported block type"))
@@ -471,8 +505,7 @@ func TestSerializeV2Block(t *testing.T) {
StateRoot: "root",
Body: &beaconBlockBodyJson{},
},
AltairBlock: nil,
Signature: "sig",
Signature: "sig",
},
}
runDefault, j, errJson := serializeV2Block(response)
@@ -495,7 +528,6 @@ func TestSerializeV2Block(t *testing.T) {
response := &blockV2ResponseJson{
Version: ethpbv2.Version_ALTAIR.String(),
Data: &signedBeaconBlockContainerV2Json{
Phase0Block: nil,
AltairBlock: &beaconBlockAltairJson{
Slot: "1",
ProposerIndex: "1",
@@ -522,6 +554,36 @@ func TestSerializeV2Block(t *testing.T) {
require.NotNil(t, beaconBlock.Body)
})
t.Run("Bellatrix", func(t *testing.T) {
response := &blockV2ResponseJson{
Version: ethpbv2.Version_BELLATRIX.String(),
Data: &signedBeaconBlockContainerV2Json{
BellatrixBlock: &beaconBlockBellatrixJson{
Slot: "1",
ProposerIndex: "1",
ParentRoot: "root",
StateRoot: "root",
Body: &beaconBlockBodyBellatrixJson{},
},
Signature: "sig",
},
}
runDefault, j, errJson := serializeV2Block(response)
require.Equal(t, nil, errJson)
require.Equal(t, apimiddleware.RunDefault(false), runDefault)
require.NotNil(t, j)
resp := &bellatrixBlockResponseJson{}
require.NoError(t, json.Unmarshal(j, resp))
require.NotNil(t, resp.Data)
require.NotNil(t, resp.Data.Message)
beaconBlock := resp.Data.Message
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 := serializeV2Block(response)

View File

@@ -100,7 +100,7 @@ func TestGetSpec(t *testing.T) {
config.TerminalBlockHash = common.HexToHash("TerminalBlockHash")
config.TerminalBlockHashActivationEpoch = 72
config.TerminalTotalDifficulty = "73"
config.FeeRecipient = common.HexToAddress("FeeRecipient")
config.DefaultFeeRecipient = common.HexToAddress("DefaultFeeRecipient")
var dbp [4]byte
copy(dbp[:], []byte{'0', '0', '0', '1'})
@@ -329,8 +329,8 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, common.HexToHash("TerminalBlockHash"), common.HexToHash(v))
case "TERMINAL_TOTAL_DIFFICULTY":
assert.Equal(t, "73", v)
case "FeeRecipient":
assert.Equal(t, common.HexToAddress("FeeRecipient"), v)
case "DefaultFeeRecipient":
assert.Equal(t, common.HexToAddress("DefaultFeeRecipient"), v)
case "PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX":
assert.Equal(t, "3", v)
case "MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX":

View File

@@ -61,6 +61,7 @@ go_test(
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/powchain/engine-api-client/v1/mocks:go_default_library",
"//beacon-chain/powchain/testing:go_default_library",
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
"//beacon-chain/rpc/testutil:go_default_library",
@@ -71,6 +72,7 @@ go_test(
"//config/params:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",

View File

@@ -252,6 +252,11 @@ func (vs *Server) ProduceBlock(ctx context.Context, req *ethpbv1.ProduceBlockReq
ctx, span := trace.StartSpan(ctx, "validator.ProduceBlock")
defer span.End()
if err := rpchelpers.ValidateSync(ctx, vs.SyncChecker, vs.HeadFetcher, vs.TimeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
block, err := vs.v1BeaconBlock(ctx, req)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block: %v", err)
@@ -263,18 +268,9 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV2")
defer span.End()
epoch := slots.ToEpoch(req.Slot)
if epoch < params.BeaconConfig().AltairForkEpoch {
block, err := vs.v1BeaconBlock(ctx, req)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_PHASE0,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_Phase0Block{Phase0Block: block},
},
}, nil
if err := rpchelpers.ValidateSync(ctx, vs.SyncChecker, vs.HeadFetcher, vs.TimeFetcher); err != nil {
// We simply return the error because it's already a gRPC error.
return nil, err
}
v1alpha1req := &ethpbalpha.BlockRequest{
@@ -287,20 +283,46 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
// We simply return err because it's already of a gRPC error type.
return nil, err
}
phase0Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Phase0)
if ok {
block, err := migration.V1Alpha1ToV1Block(phase0Block.Phase0)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_PHASE0,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_Phase0Block{Phase0Block: block},
},
}, nil
}
altairBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Altair)
if !ok {
return nil, status.Errorf(codes.Internal, "Could not get Altair block: %v", err)
if ok {
block, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlock.Altair)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_ALTAIR,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_AltairBlock{AltairBlock: block},
},
}, nil
}
block, err := migration.V1Alpha1BeaconBlockAltairToV2(altairBlock.Altair)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
bellatrixBlock, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Bellatrix)
if ok {
block, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlock.Bellatrix)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_BellatrixBlock{BellatrixBlock: block},
},
}, nil
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_ALTAIR,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_AltairBlock{AltairBlock: block},
},
}, nil
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
}
// PrepareBeaconProposer --

View File

@@ -24,6 +24,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
p2pmock "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain/engine-api-client/v1/mocks"
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/testutil"
@@ -34,6 +35,7 @@ import (
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
"github.com/prysmaticlabs/prysm/proto/migration"
@@ -610,6 +612,7 @@ func TestProduceBlock(t *testing.T) {
}
v1Server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
V1Alpha1Server: v1Alpha1Server,
}
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
@@ -641,6 +644,17 @@ func TestProduceBlock(t *testing.T) {
assert.DeepEqual(t, expectedAttSlashings, resp.Data.Body.AttesterSlashings)
}
func TestProduceBlock_SyncNotReady(t *testing.T) {
chainService := &mockChain.ChainService{}
vs := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
}
_, err := vs.ProduceBlock(context.Background(), &ethpbv1.ProduceBlockRequest{})
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
}
func TestProduceBlockV2(t *testing.T) {
t.Run("Phase 0", func(t *testing.T) {
db := dbutil.SetupDB(t)
@@ -701,6 +715,7 @@ func TestProduceBlockV2(t *testing.T) {
v1Server := &Server{
V1Alpha1Server: v1Alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
require.NoError(t, err)
@@ -834,6 +849,7 @@ func TestProduceBlockV2(t *testing.T) {
v1Server := &Server{
V1Alpha1Server: v1Alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
require.NoError(t, err)
@@ -874,6 +890,166 @@ func TestProduceBlockV2(t *testing.T) {
assert.DeepEqual(t, expectedBits, blk.Body.SyncAggregate.SyncCommitteeBits)
assert.DeepEqual(t, aggregatedSig, blk.Body.SyncAggregate.SyncCommitteeSignature)
})
t.Run("Bellatrix", func(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
params.SetupTestConfigCleanup(t)
bc := params.BeaconConfig()
bc.AltairForkEpoch = types.Epoch(0)
bc.BellatrixForkEpoch = types.Epoch(1)
params.OverrideBeaconConfig(bc)
beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, params.BeaconConfig().SyncCommitteeSize)
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch))
syncCommittee, err := altair.NextSyncCommittee(context.Background(), beaconState)
require.NoError(t, err)
require.NoError(t, beaconState.SetCurrentSyncCommittee(syncCommittee))
require.NoError(t, beaconState.SetNextSyncCommittee(syncCommittee))
stateRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state")
genesisBlock := util.NewBeaconBlockBellatrix()
genesisBlock.Block.StateRoot = stateRoot[:]
wrappedBellatrixBlock, err := wrapper.WrappedBellatrixSignedBeaconBlock(genesisBlock)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, wrappedBellatrixBlock))
parentRoot, err := genesisBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
v1Alpha1Server := &v1alpha1validator.Server{
ExecutionEngineCaller: &mocks.EngineClient{
ExecutionBlock: &enginev1.ExecutionBlock{
TotalDifficulty: "0x1",
},
},
TimeFetcher: &mockChain.ChainService{},
HeadFetcher: &mockChain.ChainService{State: beaconState, Root: parentRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
BlockReceiver: &mockChain.ChainService{},
ChainStartFetcher: &mockPOW.POWChain{},
Eth1InfoFetcher: &mockPOW.POWChain{},
Eth1BlockFetcher: &mockPOW.POWChain{},
MockEth1Votes: true,
AttPool: attestations.NewPool(),
SlashingsPool: slashings.NewPool(),
ExitPool: voluntaryexits.NewPool(),
StateGen: stategen.New(db),
SyncCommitteePool: synccommittee.NewStore(),
}
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings)
for i := types.ValidatorIndex(0); uint64(i) < params.BeaconConfig().MaxProposerSlashings; i++ {
proposerSlashing, err := util.GenerateProposerSlashingForValidator(
beaconState,
privKeys[i],
i,
)
require.NoError(t, err)
proposerSlashings[i] = proposerSlashing
err = v1Alpha1Server.SlashingsPool.InsertProposerSlashing(context.Background(), beaconState, proposerSlashing)
require.NoError(t, err)
}
attSlashings := make([]*ethpbalpha.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings)
for i := uint64(0); i < params.BeaconConfig().MaxAttesterSlashings; i++ {
attesterSlashing, err := util.GenerateAttesterSlashingForValidator(
beaconState,
privKeys[i+params.BeaconConfig().MaxProposerSlashings],
types.ValidatorIndex(i+params.BeaconConfig().MaxProposerSlashings), /* validator index */
)
require.NoError(t, err)
attSlashings[i] = attesterSlashing
err = v1Alpha1Server.SlashingsPool.InsertAttesterSlashing(context.Background(), beaconState, attesterSlashing)
require.NoError(t, err)
}
aggregationBits := bitfield.NewBitvector128()
for i := range aggregationBits {
aggregationBits[i] = 0xAA
}
syncCommitteeIndices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState)
require.NoError(t, err)
sigs := make([]bls.Signature, 0, len(syncCommitteeIndices))
for i, indice := range syncCommitteeIndices {
if aggregationBits.BitAt(uint64(i)) {
b := p2pType.SSZBytes(parentRoot[:])
sb, err := signing.ComputeDomainAndSign(beaconState, coreTime.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice])
require.NoError(t, err)
sig, err := bls.SignatureFromBytes(sb)
require.NoError(t, err)
sigs = append(sigs, sig)
}
}
aggregatedSig := bls.AggregateSignatures(sigs).Marshal()
contribution := &ethpbalpha.SyncCommitteeContribution{
Slot: params.BeaconConfig().SlotsPerEpoch,
BlockRoot: parentRoot[:],
SubcommitteeIndex: 0,
AggregationBits: aggregationBits,
Signature: aggregatedSig,
}
require.NoError(t, v1Alpha1Server.SyncCommitteePool.SaveSyncCommitteeContribution(contribution))
v1Server := &Server{
V1Alpha1Server: v1Alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
randaoReveal, err := util.RandaoReveal(beaconState, 1, privKeys)
require.NoError(t, err)
graffiti := bytesutil.ToBytes32([]byte("eth2"))
req := &ethpbv1.ProduceBlockRequest{
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
RandaoReveal: randaoReveal,
Graffiti: graffiti[:],
}
resp, err := v1Server.ProduceBlockV2(ctx, req)
require.NoError(t, err)
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
containerBlock, ok := resp.Data.Block.(*ethpbv2.BeaconBlockContainerV2_BellatrixBlock)
require.Equal(t, true, ok)
blk := containerBlock.BellatrixBlock
assert.Equal(t, req.Slot, blk.Slot, "Expected block to have slot of 1")
assert.DeepEqual(t, parentRoot[:], blk.ParentRoot, "Expected block to have correct parent root")
assert.DeepEqual(t, randaoReveal, blk.Body.RandaoReveal, "Expected block to have correct randao reveal")
assert.DeepEqual(t, req.Graffiti, blk.Body.Graffiti, "Expected block to have correct graffiti")
assert.Equal(t, params.BeaconConfig().MaxProposerSlashings, uint64(len(blk.Body.ProposerSlashings)))
expectedPropSlashings := make([]*ethpbv1.ProposerSlashing, len(proposerSlashings))
for i, slash := range proposerSlashings {
expectedPropSlashings[i] = migration.V1Alpha1ProposerSlashingToV1(slash)
}
assert.DeepEqual(t, expectedPropSlashings, blk.Body.ProposerSlashings)
assert.Equal(t, params.BeaconConfig().MaxAttesterSlashings, uint64(len(blk.Body.AttesterSlashings)))
expectedAttSlashings := make([]*ethpbv1.AttesterSlashing, len(attSlashings))
for i, slash := range attSlashings {
expectedAttSlashings[i] = migration.V1Alpha1AttSlashingToV1(slash)
}
assert.DeepEqual(t, expectedAttSlashings, blk.Body.AttesterSlashings)
expectedBits := bitfield.NewBitvector512()
for i := 0; i <= 15; i++ {
expectedBits[i] = 0xAA
}
assert.DeepEqual(t, expectedBits, blk.Body.SyncAggregate.SyncCommitteeBits)
assert.DeepEqual(t, aggregatedSig, blk.Body.SyncAggregate.SyncCommitteeSignature)
})
}
func TestProduceBlockV2_SyncNotReady(t *testing.T) {
chainService := &mockChain.ChainService{}
vs := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
}
_, err := vs.ProduceBlockV2(context.Background(), &ethpbv1.ProduceBlockRequest{})
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
}
func TestProduceAttestationData(t *testing.T) {

View File

@@ -42,6 +42,7 @@ go_library(
"//beacon-chain/core/transition/interop:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
@@ -117,6 +118,7 @@ go_test(
"//beacon-chain/cache/depositcache:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/execution:go_default_library",
"//beacon-chain/core/feed:go_default_library",
"//beacon-chain/core/feed/block:go_default_library",
"//beacon-chain/core/feed/operation:go_default_library",

View File

@@ -187,7 +187,7 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
}
// Are the validators in current or next epoch sync committee.
if ok && coreTime.AltairCompatible(s, req.Epoch) {
if ok && coreTime.HigherEqualThanAltairVersionAndEpoch(s, req.Epoch) {
assignment.IsSyncCommittee, err = helpers.IsCurrentPeriodSyncCommittee(s, idx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not determine current epoch sync committee: %v", err)

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/beacon-chain/core/execution"
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
@@ -200,6 +201,111 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
}
}
func TestGetBellatrixDuties_SyncCommitteeOK(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.MainnetConfig()
cfg.AltairForkEpoch = types.Epoch(0)
cfg.BellatrixForkEpoch = types.Epoch(1)
params.OverrideBeaconConfig(cfg)
genesis := util.NewBeaconBlock()
deposits, _, err := util.DeterministicDepositsAndKeys(params.BeaconConfig().SyncCommitteeSize)
require.NoError(t, err)
eth1Data, err := util.DeterministicEth1Data(len(deposits))
require.NoError(t, err)
bs, err := util.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
h := &ethpb.BeaconBlockHeader{
StateRoot: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentRoot: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
BodyRoot: bytesutil.PadTo([]byte{'c'}, fieldparams.RootLength),
}
require.NoError(t, bs.SetLatestBlockHeader(h))
require.NoError(t, err, "Could not setup genesis bs")
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
syncCommittee, err := altair.NextSyncCommittee(context.Background(), bs)
require.NoError(t, err)
require.NoError(t, bs.SetCurrentSyncCommittee(syncCommittee))
pubKeys := make([][]byte, len(deposits))
indices := make([]uint64, len(deposits))
for i := 0; i < len(deposits); i++ {
pubKeys[i] = deposits[i].Data.PublicKey
indices[i] = uint64(i)
}
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch*types.Slot(params.BeaconConfig().EpochsPerSyncCommitteePeriod)-1))
require.NoError(t, helpers.UpdateSyncCommitteeCache(bs))
bs, err = execution.UpgradeToBellatrix(context.Background(), bs)
require.NoError(t, err)
pubkeysAs48ByteType := make([][fieldparams.BLSPubkeyLength]byte, len(pubKeys))
for i, pk := range pubKeys {
pubkeysAs48ByteType[i] = bytesutil.ToBytes48(pk)
}
slot := uint64(params.BeaconConfig().SlotsPerEpoch) * uint64(params.BeaconConfig().EpochsPerSyncCommitteePeriod) * params.BeaconConfig().SecondsPerSlot
chain := &mockChain.ChainService{
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockPOW.POWChain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
// Test the first validator in registry.
req := &ethpb.DutiesRequest{
PublicKeys: [][]byte{deposits[0].Data.PublicKey},
}
res, err := vs.GetDuties(context.Background(), req)
require.NoError(t, err, "Could not call epoch committee assignment")
if res.CurrentEpochDuties[0].AttesterSlot > bs.Slot()+params.BeaconConfig().SlotsPerEpoch {
t.Errorf("Assigned slot %d can't be higher than %d",
res.CurrentEpochDuties[0].AttesterSlot, bs.Slot()+params.BeaconConfig().SlotsPerEpoch)
}
// Test the last validator in registry.
lastValidatorIndex := params.BeaconConfig().SyncCommitteeSize - 1
req = &ethpb.DutiesRequest{
PublicKeys: [][]byte{deposits[lastValidatorIndex].Data.PublicKey},
}
res, err = vs.GetDuties(context.Background(), req)
require.NoError(t, err, "Could not call epoch committee assignment")
if res.CurrentEpochDuties[0].AttesterSlot > bs.Slot()+params.BeaconConfig().SlotsPerEpoch {
t.Errorf("Assigned slot %d can't be higher than %d",
res.CurrentEpochDuties[0].AttesterSlot, bs.Slot()+params.BeaconConfig().SlotsPerEpoch)
}
// We request for duties for all validators.
req = &ethpb.DutiesRequest{
PublicKeys: pubKeys,
Epoch: 0,
}
res, err = vs.GetDuties(context.Background(), req)
require.NoError(t, err, "Could not call epoch committee assignment")
for i := 0; i < len(res.CurrentEpochDuties); i++ {
assert.Equal(t, types.ValidatorIndex(i), res.CurrentEpochDuties[i].ValidatorIndex)
}
for i := 0; i < len(res.CurrentEpochDuties); i++ {
assert.Equal(t, true, res.CurrentEpochDuties[i].IsSyncCommittee)
// Current epoch and next epoch duties should be equal before the sync period epoch boundary.
assert.Equal(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
}
// Current epoch and next epoch duties should not be equal at the sync period epoch boundary.
req = &ethpb.DutiesRequest{
PublicKeys: pubKeys,
Epoch: params.BeaconConfig().EpochsPerSyncCommitteePeriod - 1,
}
res, err = vs.GetDuties(context.Background(), req)
require.NoError(t, err, "Could not call epoch committee assignment")
for i := 0; i < len(res.CurrentEpochDuties); i++ {
assert.NotEqual(t, res.CurrentEpochDuties[i].IsSyncCommittee, res.NextEpochDuties[i].IsSyncCommittee)
}
}
func TestGetAltairDuties_UnknownPubkey(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.MainnetConfig()

View File

@@ -16,7 +16,7 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
return nil, err
}
payload, err := vs.getExecutionPayload(ctx, req.Slot)
payload, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex)
if err != nil {
return nil, err
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -24,7 +25,7 @@ import (
// This returns the execution payload of a given slot. The function has full awareness of pre and post merge.
// The payload is computed given the respected time of merge.
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot) (*enginev1.ExecutionPayload, error) {
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx types.ValidatorIndex) (*enginev1.ExecutionPayload, error) {
st, err := vs.HeadFetcher.HeadState(ctx)
if err != nil {
return nil, err
@@ -94,10 +95,24 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot) (*en
SafeBlockHash: parentHash,
FinalizedBlockHash: finalizedBlockHash,
}
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
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")
default:
return nil, errors.Wrap(err, "could not get fee recipient in db")
}
p := &enginev1.PayloadAttributes{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: params.BeaconConfig().FeeRecipient.Bytes(),
SuggestedFeeRecipient: feeRecipient.Bytes(),
}
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, p)
if err != nil {
@@ -204,6 +219,8 @@ func (vs *Server) getPowBlockHashAtTerminalTotalDifficulty(ctx context.Context)
}).Info("Retrieved terminal block hash")
return blk.Hash, true, nil
}
} else {
return nil, false, nil
}
blk = parentBlk
}

View File

@@ -79,6 +79,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
require.NoError(t, beaconDB.SaveBlock(context.Background(), b1))
require.NoError(t, beaconDB.SaveBlock(context.Background(), b2))
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []types.ValidatorIndex{0}, []common.Address{{}}))
tests := []struct {
name string
@@ -88,6 +89,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
payloadID *pb.PayloadIDBytes
terminalBlockHash common.Hash
activationEpoch types.Epoch
validatorIndx types.ValidatorIndex
}{
{
name: "transition completed, nil payload id",
@@ -95,10 +97,16 @@ func TestServer_getExecutionPayload(t *testing.T) {
errString: "nil payload id",
},
{
name: "transition completed, happy case",
name: "transition completed, happy case (has fee recipient in Db)",
st: transitionSt,
payloadID: &pb.PayloadIDBytes{0x1},
},
{
name: "transition completed, happy case (doesn't have fee recipient in Db)",
st: transitionSt,
payloadID: &pb.PayloadIDBytes{0x1},
validatorIndx: 1,
},
{
name: "transition completed, could not prepare payload",
st: transitionSt,
@@ -129,7 +137,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
HeadFetcher: &chainMock.ChainService{State: tt.st},
BeaconDB: beaconDB,
}
_, err := vs.getExecutionPayload(context.Background(), tt.st.Slot())
_, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -225,6 +233,20 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
wantExists: true,
wantTerminalBlockHash: []byte{'a'},
},
{
name: "ttd not reached",
paramsTd: "3",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x2",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
TotalDifficulty: "0x1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -42,6 +42,7 @@ import (
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"github.com/prysmaticlabs/prysm/time/slots"
logTest "github.com/sirupsen/logrus/hooks/test"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
@@ -2233,6 +2234,7 @@ func TestProposer_GetBeaconBlock_PostForkEpoch(t *testing.T) {
func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
hook := logTest.NewGlobal()
terminalBlockHash := bytesutil.PadTo([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 32)
@@ -2333,6 +2335,7 @@ func TestProposer_GetBeaconBlock_BellatrixEpoch(t *testing.T) {
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
ExecutionPayload: payload,
},
BeaconDB: db,
}
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
@@ -2346,6 +2349,10 @@ 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)
@@ -2356,6 +2363,7 @@ 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.DeepEqual(t, payload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
}

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v1

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v1

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v1

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v1

View File

@@ -3,75 +3,43 @@ package v1
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconState{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.BlockRoots()
want := make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
want[i] = make([]byte, 32)
}
require.DeepEqual(t, want, got)
want = make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
if i == 0 {
want[i] = bytesutil.PadTo([]byte{'a'}, 32)
} else {
want[i] = make([]byte, 32)
}
}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootsNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), got)
r := [fieldparams.BlockRootsLength][32]byte{{'a'}}
bRoots := make([][]byte, len(r))
for i, root := range r {
tmp := root
bRoots[i] = tmp[:]
}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: bRoots})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{BlockRoots: BR})
},
)
}

View File

@@ -9,13 +9,13 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{Slot: 1})
})
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{CurrentJustifiedCheckpoint: cp})
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{PreviousJustifiedCheckpoint: cp})
@@ -33,7 +33,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
})
}

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})

View File

@@ -86,6 +86,7 @@ go_test(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/testing:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//beacon-chain/state/v2:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v2

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v2

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v2

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v2

View File

@@ -3,74 +3,43 @@ package v2
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.BlockRoots()
want := make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
want[i] = make([]byte, fieldparams.RootLength)
}
require.DeepEqual(t, want, got)
want = make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
if i == 0 {
want[i] = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
} else {
want[i] = make([]byte, fieldparams.RootLength)
}
}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootsNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, fieldparams.RootLength), got)
r := [fieldparams.BlockRootsLength][fieldparams.RootLength]byte{{'a'}}
bRoots := make([][]byte, len(r))
for i, root := range r {
tmp := root
bRoots[i] = tmp[:]
}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: bRoots})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}

View File

@@ -9,13 +9,13 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{Slot: 1})
})
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
@@ -33,7 +33,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
})
}

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v2.InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
Validators: nil,
})

View File

@@ -0,0 +1,223 @@
package v2
import (
"reflect"
"runtime"
"runtime/debug"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestStateReferenceSharing_Finalizer(t *testing.T) {
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{RandaoMixes: [][]byte{[]byte("foo")}})
require.NoError(t, err)
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
func() {
// Create object in a different scope for GC
b := s.Copy()
assert.Equal(t, uint(2), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
_ = b
}()
runtime.GC() // Should run finalizer on object b
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
copied := s.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || s.sharedFieldReferences[randaoMixes].Refs() != 1 {
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
}
}
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
BlockRoots: [][]byte{
root1[:],
},
StateRoots: [][]byte{
root1[:],
},
})
require.NoError(t, err)
assertRefCount(t, s, blockRoots, 1)
assertRefCount(t, s, stateRoots, 1)
// Copy, increases reference count.
copied := s.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, blockRoots, 2)
assertRefCount(t, s, stateRoots, 2)
assertRefCount(t, b, blockRoots, 2)
assertRefCount(t, b, stateRoots, 2)
assert.Equal(t, 8192, len(b.BlockRoots()), "Wrong number of block roots found")
assert.Equal(t, 8192, len(b.StateRoots()), "Wrong number of state roots found")
// Assert shared state.
blockRootsA := s.BlockRoots()
stateRootsA := s.StateRoots()
blockRootsB := b.BlockRoots()
stateRootsB := b.StateRoots()
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assertValFound(t, blockRootsA, root1[:])
assertValFound(t, blockRootsB, root1[:])
assertValFound(t, stateRootsA, root1[:])
assertValFound(t, stateRootsB, root1[:])
// Mutator should only affect calling state: a.
require.NoError(t, s.UpdateBlockRootAtIndex(0, root2))
require.NoError(t, s.UpdateStateRootAtIndex(0, root2))
// Assert no shared state mutation occurred only on state a (copy on write).
assertValNotFound(t, s.BlockRoots(), root1[:])
assertValNotFound(t, s.StateRoots(), root1[:])
assertValFound(t, s.BlockRoots(), root2[:])
assertValFound(t, s.StateRoots(), root2[:])
assertValFound(t, b.BlockRoots(), root1[:])
assertValFound(t, b.StateRoots(), root1[:])
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assert.DeepEqual(t, root2[:], s.BlockRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root2[:], s.StateRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, s, blockRoots, 1)
assertRefCount(t, s, stateRoots, 1)
assertRefCount(t, b, blockRoots, 1)
assertRefCount(t, b, stateRoots, 1)
}
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
val1, val2 := bytesutil.PadTo([]byte("foo"), 32), bytesutil.PadTo([]byte("bar"), 32)
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
RandaoMixes: [][]byte{
val1,
},
})
require.NoError(t, err)
assertRefCount(t, s, randaoMixes, 1)
// Copy, increases reference count.
copied := s.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, randaoMixes, 2)
assertRefCount(t, b, randaoMixes, 2)
// Assert shared state.
mixesA := s.RandaoMixes()
mixesB := b.RandaoMixes()
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, mixesA, val1)
assertValFound(t, mixesB, val1)
// Mutator should only affect calling state: a.
require.NoError(t, s.UpdateRandaoMixesAtIndex(0, val2))
// Assert no shared state mutation occurred only on state a (copy on write).
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, s.RandaoMixes(), val2)
assertValNotFound(t, s.RandaoMixes(), val1)
assertValFound(t, b.RandaoMixes(), val1)
assertValNotFound(t, b.RandaoMixes(), val2)
assertValFound(t, mixesB, val1)
assertValNotFound(t, mixesB, val2)
assert.DeepEqual(t, val2, s.RandaoMixes()[0], "Expected mutation not found")
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, s, randaoMixes, 1)
assertRefCount(t, b, randaoMixes, 1)
}
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
Validators: []*ethpb.Validator{
{PublicKey: []byte{'A'}},
{PublicKey: []byte{'B'}},
{PublicKey: []byte{'C'}},
{PublicKey: []byte{'D'}},
{PublicKey: []byte{'E'}},
},
})
require.NoError(t, err)
// Create a second state.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
// Update First Validator.
assert.NoError(t, a.UpdateValidatorAtIndex(0, &ethpb.Validator{PublicKey: []byte{'Z'}}))
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
// Modify all validators from copied state.
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
return true, &ethpb.Validator{PublicKey: []byte{'V'}}, nil
}))
// Ensure reference is properly accounted for.
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
return nil
}))
}
// assertRefCount checks whether reference count for a given state
// at a given index is equal to expected amount.
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
}
}
// assertValFound checks whether item with a given value exists in list.
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
}
// assertValNotFound checks whether item with a given value doesn't exist in list.
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
t.Log(string(debug.Stack()))
t.Errorf("Unexpected value found (%v),: %v", vals, val)
return
}
}
}

View File

@@ -87,6 +87,7 @@ go_test(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/testing:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//beacon-chain/state/v3:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v3

View File

@@ -1,3 +1,4 @@
//go:build !minimal
// +build !minimal
package v3

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v3

View File

@@ -1,3 +1,4 @@
//go:build minimal
// +build minimal
package v3

View File

@@ -3,75 +3,43 @@ package v3
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.BlockRoots()
want := make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
want[i] = make([]byte, fieldparams.RootLength)
}
require.DeepEqual(t, want, got)
want = make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
if i == 0 {
want[i] = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
} else {
want[i] = make([]byte, fieldparams.RootLength)
}
}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootsNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, fieldparams.RootLength), got)
r := [fieldparams.BlockRootsLength][fieldparams.RootLength]byte{{'a'}}
bRoots := make([][]byte, len(r))
for i, root := range r {
tmp := root
bRoots[i] = tmp[:]
}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: bRoots})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: BR})
},
)
}

View File

@@ -9,13 +9,13 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{Slot: 1})
})
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{CurrentJustifiedCheckpoint: cp})
@@ -24,7 +24,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckptNative(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{PreviousJustifiedCheckpoint: cp})

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v3.InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{
Validators: nil,
})

View File

@@ -0,0 +1,229 @@
package v3
import (
"reflect"
"runtime"
"runtime/debug"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestStateReferenceSharing_Finalizer(t *testing.T) {
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{RandaoMixes: [][]byte{[]byte("foo")}})
require.NoError(t, err)
s, ok := a.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
func() {
// Create object in a different scope for GC
b := a.Copy()
assert.Equal(t, uint(2), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
_ = b
}()
runtime.GC() // Should run finalizer on object b
assert.Equal(t, uint(1), s.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || s.sharedFieldReferences[randaoMixes].Refs() != 1 {
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
}
}
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{
BlockRoots: [][]byte{
root1[:],
},
StateRoots: [][]byte{
root1[:],
},
})
require.NoError(t, err)
s, ok := a.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, blockRoots, 1)
assertRefCount(t, s, stateRoots, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, blockRoots, 2)
assertRefCount(t, s, stateRoots, 2)
assertRefCount(t, b, blockRoots, 2)
assertRefCount(t, b, stateRoots, 2)
assert.Equal(t, 8192, len(b.BlockRoots()), "Wrong number of block roots found")
assert.Equal(t, 8192, len(b.StateRoots()), "Wrong number of state roots found")
// Assert shared state.
blockRootsA := a.BlockRoots()
stateRootsA := a.StateRoots()
blockRootsB := b.BlockRoots()
stateRootsB := b.StateRoots()
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assertValFound(t, blockRootsA, root1[:])
assertValFound(t, blockRootsB, root1[:])
assertValFound(t, stateRootsA, root1[:])
assertValFound(t, stateRootsB, root1[:])
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
// Assert no shared state mutation occurred only on state a (copy on write).
assertValNotFound(t, a.BlockRoots(), root1[:])
assertValNotFound(t, a.StateRoots(), root1[:])
assertValFound(t, a.BlockRoots(), root2[:])
assertValFound(t, a.StateRoots(), root2[:])
assertValFound(t, b.BlockRoots(), root1[:])
assertValFound(t, b.StateRoots(), root1[:])
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, s, blockRoots, 1)
assertRefCount(t, s, stateRoots, 1)
assertRefCount(t, b, blockRoots, 1)
assertRefCount(t, b, stateRoots, 1)
}
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
val1, val2 := bytesutil.PadTo([]byte("foo"), 32), bytesutil.PadTo([]byte("bar"), 32)
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{
RandaoMixes: [][]byte{
val1,
},
})
require.NoError(t, err)
s, ok := a.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, randaoMixes, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, randaoMixes, 2)
assertRefCount(t, b, randaoMixes, 2)
// Assert shared state.
mixesA := a.RandaoMixes()
mixesB := b.RandaoMixes()
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, mixesA, val1)
assertValFound(t, mixesB, val1)
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
// Assert no shared state mutation occurred only on state a (copy on write).
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, a.RandaoMixes(), val2)
assertValNotFound(t, a.RandaoMixes(), val1)
assertValFound(t, b.RandaoMixes(), val1)
assertValNotFound(t, b.RandaoMixes(), val2)
assertValFound(t, mixesB, val1)
assertValNotFound(t, mixesB, val2)
assert.DeepEqual(t, val2, a.RandaoMixes()[0], "Expected mutation not found")
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, s, randaoMixes, 1)
assertRefCount(t, b, randaoMixes, 1)
}
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{
Validators: []*ethpb.Validator{
{PublicKey: []byte{'A'}},
{PublicKey: []byte{'B'}},
{PublicKey: []byte{'C'}},
{PublicKey: []byte{'D'}},
{PublicKey: []byte{'E'}},
},
})
require.NoError(t, err)
// Create a second state.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
// Update First Validator.
assert.NoError(t, a.UpdateValidatorAtIndex(0, &ethpb.Validator{PublicKey: []byte{'Z'}}))
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
// Modify all validators from copied state.
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
return true, &ethpb.Validator{PublicKey: []byte{'V'}}, nil
}))
// Ensure reference is properly accounted for.
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
return nil
}))
}
// assertRefCount checks whether reference count for a given state
// at a given index is equal to expected amount.
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
}
}
// assertValFound checks whether item with a given value exists in list.
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
}
// assertValNotFound checks whether item with a given value doesn't exist in list.
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
t.Log(string(debug.Stack()))
t.Errorf("Unexpected value found (%v),: %v", vals, val)
return
}
}
}

View File

@@ -5,6 +5,7 @@ go_library(
testonly = True,
srcs = [
"getters.go",
"getters_block.go",
"getters_validator.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/testing",
@@ -12,6 +13,7 @@ go_library(
deps = [
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",

View File

@@ -12,7 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/testing/require"
)
func VerifyBeaconState_SlotDataRace(t *testing.T, factory getState) {
func VerifyBeaconStateSlotDataRace(t *testing.T, factory getState) {
headState, err := factory()
require.NoError(t, err)
@@ -33,7 +33,7 @@ func VerifyBeaconState_SlotDataRace(t *testing.T, factory getState) {
type getStateWithCurrentJustifiedCheckpoint func(*ethpb.Checkpoint) (state.BeaconState, error)
type clearInternalState func(state.BeaconState)
func VerifyBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
func VerifyBeaconStateMatchCurrentJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := factory(c1)
@@ -46,7 +46,7 @@ func VerifyBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T, factory getSta
require.Equal(t, false, beaconState.MatchCurrentJustifiedCheckpoint(c1))
}
func VerifyBeaconState_MatchCurrentJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
func VerifyBeaconStateMatchCurrentJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := factory(c1)
@@ -57,7 +57,7 @@ func VerifyBeaconState_MatchCurrentJustifiedCheckptNative(t *testing.T, factory
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
}
func VerifyBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
func VerifyBeaconStateMatchPreviousJustifiedCheckpt(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint, clear clearInternalState) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := factory(c1)
@@ -70,7 +70,7 @@ func VerifyBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T, factory getSt
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c1))
}
func VerifyBeaconState_MatchPreviousJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
func VerifyBeaconStateMatchPreviousJustifiedCheckptNative(t *testing.T, factory getStateWithCurrentJustifiedCheckpoint) {
c1 := &ethpb.Checkpoint{Epoch: 1}
c2 := &ethpb.Checkpoint{Epoch: 2}
beaconState, err := factory(c1)
@@ -81,7 +81,7 @@ func VerifyBeaconState_MatchPreviousJustifiedCheckptNative(t *testing.T, factory
require.Equal(t, false, beaconState.MatchPreviousJustifiedCheckpoint(c2))
}
func VerifyBeaconState_MarshalSSZ_NilState(t *testing.T, factory getState, clear clearInternalState) {
func VerifyBeaconStateMarshalSSZNilState(t *testing.T, factory getState, clear clearInternalState) {
s, err := factory()
require.NoError(t, err)
clear(s)
@@ -89,7 +89,7 @@ func VerifyBeaconState_MarshalSSZ_NilState(t *testing.T, factory getState, clear
require.ErrorContains(t, "nil beacon state", err)
}
func VerifyBeaconState_ValidatorByPubkey(t *testing.T, factory getState) {
func VerifyBeaconStateValidatorByPubkey(t *testing.T, factory getState) {
keyCreator := func(input []byte) [fieldparams.BLSPubkeyLength]byte {
nKey := [fieldparams.BLSPubkeyLength]byte{}
copy(nKey[:1], input)

View File

@@ -0,0 +1,135 @@
package testing
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
type getStateWithLatestBlockHeader func(*ethpb.BeaconBlockHeader) (state.BeaconState, error)
func VerifyBeaconStateLatestBlockHeader(
t *testing.T,
factory getState,
factoryLBH getStateWithLatestBlockHeader,
) {
s, err := factory()
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = factoryLBH(want)
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
}
type getStateWithLBlockRoots func([][]byte) (state.BeaconState, error)
func VerifyBeaconStateBlockRoots(
t *testing.T,
factory getState,
factoryBR getStateWithLBlockRoots,
) {
s, err := factory()
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = factoryBR(want)
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
}
func VerifyBeaconStateBlockRootsNative(
t *testing.T,
factory getState,
factoryBR getStateWithLBlockRoots,
) {
s, err := factory()
require.NoError(t, err)
got := s.BlockRoots()
want := make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
want[i] = make([]byte, 32)
}
require.DeepEqual(t, want, got)
want = make([][]byte, fieldparams.BlockRootsLength)
for i := range want {
if i == 0 {
want[i] = bytesutil.PadTo([]byte{'a'}, 32)
} else {
want[i] = make([]byte, 32)
}
}
s, err = factoryBR(want)
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
}
func VerifyBeaconStateBlockRootAtIndex(
t *testing.T,
factory getState,
factoryBR getStateWithLBlockRoots,
) {
s, err := factory()
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = factoryBR(r)
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
require.DeepSSZEqual(t, want, got)
}
func VerifyBeaconStateBlockRootAtIndexNative(
t *testing.T,
factory getState,
factoryBR getStateWithLBlockRoots,
) {
s, err := factory()
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), got)
r := [fieldparams.BlockRootsLength][32]byte{{'a'}}
bRoots := make([][]byte, len(r))
for i, root := range r {
tmp := root
bRoots[i] = tmp[:]
}
s, err = factoryBR(bRoots)
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
}

View File

@@ -10,7 +10,7 @@ import (
type getState func() (state.BeaconState, error)
func VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T, factory getState) {
func VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t *testing.T, factory getState) {
st, err := factory()
require.NoError(t, err)

View File

@@ -3,57 +3,43 @@ package v1
import (
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconState{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRoots(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconState{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndex(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{BlockRoots: BR})
},
)
}

View File

@@ -12,7 +12,7 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{Slot: 1})
})
}
@@ -66,7 +66,7 @@ func TestNilState_NoPanic(t *testing.T) {
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{CurrentJustifiedCheckpoint: cp})
@@ -82,7 +82,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{PreviousJustifiedCheckpoint: cp})
@@ -98,7 +98,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
testtmpl.VerifyBeaconState_MarshalSSZ_NilState(
testtmpl.VerifyBeaconStateMarshalSSZNilState(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
@@ -114,7 +114,7 @@ func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
})
}

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})

View File

@@ -68,6 +68,7 @@ go_test(
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"references_test.go",
"setters_test.go",
"state_trie_test.go",
],

View File

@@ -7,6 +7,13 @@ import (
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
_, err = s.PreviousEpochAttestations()
require.ErrorContains(t, "PreviousEpochAttestations is not supported for hard fork 1 beacon state", err)
}
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
@@ -14,9 +21,9 @@ func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
require.ErrorContains(t, "CurrentEpochAttestations is not supported for hard fork 1 beacon state", err)
}
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
func TestBeaconState_LatestExecutionPayloadHeader(t *testing.T) {
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
_, err = s.PreviousEpochAttestations()
require.ErrorContains(t, "PreviousEpochAttestations is not supported for hard fork 1 beacon state", err)
_, err = s.LatestExecutionPayloadHeader()
require.ErrorContains(t, "LatestExecutionPayloadHeader is not supported for hard fork 1 beacon state", err)
}

View File

@@ -3,58 +3,43 @@ package v2
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRoots(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateAltair{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndex(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}

View File

@@ -11,7 +11,7 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{Slot: 1})
})
}
@@ -75,7 +75,7 @@ func TestNilState_NoPanic(t *testing.T) {
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
@@ -91,7 +91,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
@@ -107,7 +107,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
testtmpl.VerifyBeaconState_MarshalSSZ_NilState(
testtmpl.VerifyBeaconStateMarshalSSZNilState(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
@@ -123,7 +123,7 @@ func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
})
}

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v2.InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
Validators: nil,
})

View File

@@ -0,0 +1,234 @@
package v2
import (
"reflect"
"runtime"
"runtime/debug"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestStateReferenceSharing_Finalizer(t *testing.T) {
// This test showcases the logic on a the RandaoMixes field with the GC finalizer.
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{RandaoMixes: [][]byte{[]byte("foo")}})
require.NoError(t, err)
a, ok := s.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(1), a.sharedFieldReferences[randaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
func() {
// Create object in a different scope for GC
b := a.Copy()
assert.Equal(t, uint(2), a.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
_ = b
}()
runtime.GC() // Should run finalizer on object b
assert.Equal(t, uint(1), a.sharedFieldReferences[randaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assert.Equal(t, uint(2), b.sharedFieldReferences[randaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar")))
if b.sharedFieldReferences[randaoMixes].Refs() != 1 || a.sharedFieldReferences[randaoMixes].Refs() != 1 {
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
}
}
func TestStateReferenceCopy_NoUnexpectedRootsMutation(t *testing.T) {
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
BlockRoots: [][]byte{
root1[:],
},
StateRoots: [][]byte{
root1[:],
},
})
require.NoError(t, err)
a, ok := s.(*BeaconState)
require.Equal(t, true, ok)
require.NoError(t, err)
assertRefCount(t, a, blockRoots, 1)
assertRefCount(t, a, stateRoots, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, a, blockRoots, 2)
assertRefCount(t, a, stateRoots, 2)
assertRefCount(t, b, blockRoots, 2)
assertRefCount(t, b, stateRoots, 2)
assert.Equal(t, 1, len(b.state.GetBlockRoots()), "No block roots found")
assert.Equal(t, 1, len(b.state.GetStateRoots()), "No state roots found")
// Assert shared state.
blockRootsA := a.state.GetBlockRoots()
stateRootsA := a.state.GetStateRoots()
blockRootsB := b.state.GetBlockRoots()
stateRootsB := b.state.GetStateRoots()
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assertValFound(t, blockRootsA, root1[:])
assertValFound(t, blockRootsB, root1[:])
assertValFound(t, stateRootsA, root1[:])
assertValFound(t, stateRootsB, root1[:])
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
// Assert no shared state mutation occurred only on state a (copy on write).
assertValNotFound(t, a.state.GetBlockRoots(), root1[:])
assertValNotFound(t, a.state.GetStateRoots(), root1[:])
assertValFound(t, a.state.GetBlockRoots(), root2[:])
assertValFound(t, a.state.GetStateRoots(), root2[:])
assertValFound(t, b.state.GetBlockRoots(), root1[:])
assertValFound(t, b.state.GetStateRoots(), root1[:])
if len(blockRootsA) != len(blockRootsB) || len(blockRootsA) < 1 {
t.Errorf("Unexpected number of block roots, want: %v", 1)
}
if len(stateRootsA) != len(stateRootsB) || len(stateRootsA) < 1 {
t.Errorf("Unexpected number of state roots, want: %v", 1)
}
assert.DeepEqual(t, root2[:], a.state.GetBlockRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root2[:], a.state.GetStateRoots()[0], "Expected mutation not found")
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, a, blockRoots, 1)
assertRefCount(t, a, stateRoots, 1)
assertRefCount(t, b, blockRoots, 1)
assertRefCount(t, b, stateRoots, 1)
}
func TestStateReferenceCopy_NoUnexpectedRandaoMutation(t *testing.T) {
val1, val2 := []byte("foo"), []byte("bar")
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
RandaoMixes: [][]byte{
val1,
},
})
require.NoError(t, err)
a, ok := s.(*BeaconState)
require.Equal(t, true, ok)
require.NoError(t, err)
assertRefCount(t, a, randaoMixes, 1)
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, a, randaoMixes, 2)
assertRefCount(t, b, randaoMixes, 2)
assert.Equal(t, 1, len(b.state.GetRandaoMixes()), "No randao mixes found")
// Assert shared state.
mixesA := a.state.GetRandaoMixes()
mixesB := b.state.GetRandaoMixes()
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, mixesA, val1)
assertValFound(t, mixesB, val1)
// Mutator should only affect calling state: a.
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
// Assert no shared state mutation occurred only on state a (copy on write).
if len(mixesA) != len(mixesB) || len(mixesA) < 1 {
t.Errorf("Unexpected number of mix values, want: %v", 1)
}
assertValFound(t, a.state.GetRandaoMixes(), val2)
assertValNotFound(t, a.state.GetRandaoMixes(), val1)
assertValFound(t, b.state.GetRandaoMixes(), val1)
assertValNotFound(t, b.state.GetRandaoMixes(), val2)
assertValFound(t, mixesB, val1)
assertValNotFound(t, mixesB, val2)
assert.DeepEqual(t, val2, a.state.GetRandaoMixes()[0], "Expected mutation not found")
assert.DeepEqual(t, val1, mixesB[0], "Unexpected mutation found")
// Copy on write happened, reference counters are reset.
assertRefCount(t, a, randaoMixes, 1)
assertRefCount(t, b, randaoMixes, 1)
}
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
s, err := InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{
Validators: []*ethpb.Validator{
{PublicKey: []byte{'A'}},
{PublicKey: []byte{'B'}},
{PublicKey: []byte{'C'}},
{PublicKey: []byte{'D'}},
{PublicKey: []byte{'E'}},
},
})
require.NoError(t, err)
a, ok := s.(*BeaconState)
require.Equal(t, true, ok)
// Create a second state.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
// Update First Validator.
assert.NoError(t, a.UpdateValidatorAtIndex(0, &ethpb.Validator{PublicKey: []byte{'Z'}}))
assert.DeepNotEqual(t, a.state.Validators[0], b.state.Validators[0], "validators are equal when they are supposed to be different")
// Modify all validators from copied state.
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
return true, &ethpb.Validator{PublicKey: []byte{'V'}}, nil
}))
// Ensure reference is properly accounted for.
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
return nil
}))
}
// assertRefCount checks whether reference count for a given state
// at a given index is equal to expected amount.
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
}
}
// assertValFound checks whether item with a given value exists in list.
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
}
// assertValNotFound checks whether item with a given value doesn't exist in list.
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
for i := range vals {
if reflect.DeepEqual(vals[i], val) {
t.Log(string(debug.Stack()))
t.Errorf("Unexpected value found (%v),: %v", vals, val)
return
}
}
}

View File

@@ -69,6 +69,7 @@ go_test(
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"references_test.go",
"setters_test.go",
"state_trie_test.go",
],

View File

@@ -3,57 +3,43 @@ package v3
import (
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.LatestBlockHeader()
require.DeepEqual(t, (*ethpb.BeaconBlockHeader)(nil), got)
want := &ethpb.BeaconBlockHeader{Slot: 100}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{LatestBlockHeader: want})
require.NoError(t, err)
got = s.LatestBlockHeader()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got.Slot = 101
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateLatestBlockHeader(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got := s.BlockRoots()
require.DeepEqual(t, ([][]byte)(nil), got)
want := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: want})
require.NoError(t, err)
got = s.BlockRoots()
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0][0] = 'b'
require.DeepNotEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRoots(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
got, err := s.BlockRootAtIndex(0)
require.NoError(t, err)
require.DeepEqual(t, ([]byte)(nil), got)
r := [][]byte{{'a'}}
s, err = InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: r})
require.NoError(t, err)
got, err = s.BlockRootAtIndex(0)
require.NoError(t, err)
want := bytesutil.PadTo([]byte{'a'}, 32)
require.DeepSSZEqual(t, want, got)
testtmpl.VerifyBeaconStateBlockRootAtIndex(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{BlockRoots: BR})
},
)
}

View File

@@ -11,7 +11,7 @@ import (
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconState_SlotDataRace(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{Slot: 1})
})
}
@@ -75,7 +75,7 @@ func TestNilState_NoPanic(t *testing.T) {
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchCurrentJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{CurrentJustifiedCheckpoint: cp})
@@ -91,7 +91,7 @@ func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconState_MatchPreviousJustifiedCheckpt(
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckpt(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{PreviousJustifiedCheckpoint: cp})
@@ -107,7 +107,7 @@ func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
}
func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
testtmpl.VerifyBeaconState_MarshalSSZ_NilState(
testtmpl.VerifyBeaconStateMarshalSSZNilState(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
@@ -123,7 +123,7 @@ func TestBeaconState_MarshalSSZ_NilState(t *testing.T) {
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorByPubkey(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateBellatrix{})
})
}

View File

@@ -10,7 +10,7 @@ import (
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t, func() (state.BeaconState, error) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v3.InitializeFromProtoUnsafe(&ethpb.BeaconStateBellatrix{
Validators: nil,
})

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