Compare commits

...

21 Commits

Author SHA1 Message Date
nisdas
3e97651512 try again 2022-04-15 18:09:28 +08:00
nisdas
5cb1cb2e15 delete sub-dirs 2022-04-15 18:04:52 +08:00
nisdas
b3cb2770df Revert "delete sub-dirs"
This reverts commit a12f6ae30a.
2022-04-15 18:04:15 +08:00
nisdas
a12f6ae30a delete sub-dirs 2022-04-15 17:57:46 +08:00
Preston Van Loon
cc23b8311a Static analysis: gocognit (#10527)
* Add gocognit to static analyzers with a very high threshold

* edit readme and sort analyzers
2022-04-15 06:29:07 +00:00
terence tsao
cbe54fe3f9 Handle nil execution block response when logging TTD (#10502) 2022-04-13 19:47:04 -07:00
Nishant Das
1b6adca3ca Handle Finalized Deposit Insertion Better (#10517) 2022-04-13 10:08:19 +02:00
Nishant Das
1651649e5a Update to 1.17.9 (#10518) 2022-04-13 05:25:13 +00:00
Potuz
56187edb98 Use forkchoice first when checking canonical status (#10516) 2022-04-12 21:34:13 -03:00
Preston Van Loon
ecad5bbffc e2e: Provide e2e config yaml to web3signer (#10123)
* e2e: Provide e2e config yaml to web3signer

* fix build for //testing/endtoend:go_default_test

* Update with web3signer with teku fixes

* buildifier

* Add slasher case for web3signer

* Update testing/endtoend/minimal_e2e_test.go

* Update web3signer to 21.10.6

* Revert "Update web3signer to 21.10.6"

This reverts commit bdf3c408f2.

* Remove slasher part of web3signer e2e tests

* Revert "Remove slasher part of web3signer e2e tests"

This reverts commit 24249802ae.

* fix slasher web3signer test

* fixing build

* updating yaml to match testnet_e2e_config.go

* trying a different order to the e2e test and adding a log

* trying different way to kill process

* handling unhandled error

* testing changes to config WIP

* fixing bazel WIP

* fixing build

* ignoring test for now to test

* fixing bazel

* Test only web3signer e2e

* rolling back some commits to test

* fixing bazel

* trying an updated web3signer version

* changing flag to match version

* trying current version of develop for web3signer

* testing not using the --network property

* addressing build error

* testing config change

* reverting to go bakc to using the network file

* testing adding epochs per sync committee period

* rolling back configs

* removing check to test

* adding log to get sync committee duties and changing bellatrix fork epoch to something large and altair to epoch 1

* fixing bazel

* updating epoch in config file

* fixing more descrepencies between the configurations

* removing un used yaml

* removing goland added duplicates

* reverting using network minimal

* fixing bug

* rolling back some changes

* rolling back some changes

* rolling back changes

* making sure web3signer test doesn't make it to bellatrix fork yet

* reverting changes I did not touch

* undo comment

* Update testing/endtoend/deps.bzl

* Apply suggestions from code review

* rm nl

* fix //testing/endtoend:go_mainnet_test

* Remove unnecessary dep

* fix //testing/endtoend:go_mainnet_test

* addressing review comments

* fixing build and internal conflict

* removing web3signer slasher test as it's unneeded due to the interface nature of key signing, the regular slashing test is enough

* fix: The validator we fetch from the binary can only run before altair, if you add that and the web3signer then these things can never run together as the web3signer sets it to before bellatrix

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: James He <james@prysmaticlabs.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2022-04-12 16:57:46 +00:00
Nishant Das
407182387b fix it all (#10511) 2022-04-12 19:50:29 +08:00
terence tsao
ad0b0b503d Move GetTerminalBlockHash to powchain engine (#10500)
* Move GetTerminalBlockHash to powchain engine

* Update service.go

* Update service.go

Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-04-12 10:19:07 +00:00
terence tsao
58f4ba758c Metrics tracking EE VALID/SYNCING/INVALID response counter (#10504)
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2022-04-12 09:51:13 +00:00
james-prysm
64f64f06bf Remote Key Manager API(web3signer) (#10302)
* removing flag requirement, can run web3signer without predefined public keys

* placeholders for remote-keymanager-api

* adding proto and accountschangedfeed

* updating generated code

* fix imports

* fixing interface

* adding work in progress apimiddleware code

* started implementing functions for remote keymanager api

* fixing generted code from proto

* fixing protos

* fixing import format

* fixing proto generation again , didn't fix the first time

* fixing imports again

* continuing on implementing functions

* implementing add function

* implementing delete API function

* handling errors for API

* removing unusedcode and fixing format

* fixing bazel

* wip enable --web when running web3signer

* fixing wallet check for web3signer

* fixing apis

* adding list remote keys unit test

* import remote keys test

* delete pubkeys tests

* moving location of tests

* adding unit tests

* adding placeholder functions

* adding more unit tests

* fixing bazel

* fixing build

* fixing already slice issue with unit test

* fixing linting

* Update validator/client/validator.go

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

* Update validator/keymanager/types.go

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

* Update validator/node/node.go

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

* Update validator/keymanager/types.go

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

* Update validator/client/validator.go

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

* adding comment on proto based on review

* Update validator/keymanager/remote-web3signer/keymanager.go

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

* Update validator/keymanager/remote-web3signer/keymanager.go

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

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* adding generated code based on review

* updating based on feedback

* fixing imports

* fixing formatting

* Update validator/rpc/standard_api.go

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

* fixing event call

* fixing dependency

* updating bazel

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* Update validator/rpc/standard_api.go

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

* addressing comment from review

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2022-04-11 16:05:40 -04:00
terence tsao
e70055733f Save state to DB after proposer boost (#10509) 2022-04-11 16:51:49 +00:00
Radosław Kapka
36e4f49af0 Bellatrix evaluators (#10506)
* defensive nil check

* separate ExecutionPayload/Header from codegen

* tell bazel about this new file

* Merge: support terminal difficulty override (#9769)

* Fix finding terminal block hash calculation

* Update mainnet_config.go

* Update beacon_block.pb.go

* Various fixes to pass all spec tests for Merge (#9777)

* Proper upgrade altair to merge state

* Use uint64 for ttd

* Correctly upgrade to merge state + object mapping fixes

* Use proper receive block path for initial syncing

* Disable contract lookback

* Disable deposit contract lookback

* Go fmt

* Merge: switch from go bindings to raw rpc calls (#9803)

* Disable genesis ETH1.0 chain header logging

* Update htrutils.go

* all gossip tests passing

* Remove gas validations

* Update penalty params for Merge

* Fix gossip and tx size limits for the merge part 1

* Remove extraneous p2p condition

* Add and use

* Add and use TBH_ACTIVATION_EPOCH

* Update WORKSPACE

* Update Kintsugi engine API (#9865)

* Kintsugi ssz (#9867)

* All spec tests pass

* Update spec test shas

* Update Kintsugi consensus implementations (#9872)

* Remove secp256k1

* Remove unused merge genesis state gen tool

* Manually override nil transaction field. M2 works

* Fix bad hex conversion

* Change Gossip message size and Chunk SIze from 1 MB t0 10MB (#9860)

* change gossip size and chunk size after merge

* change ssz to accomodate both changes

* gofmt config file

* add testcase for merge MsgId

* Update beacon-chain/p2p/message_id.go

Change MB to Mib in comment

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

* change function name from altairMsgID to postAltairMsgID

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

* Sync with develop

* Merge branch 'develop' of github.com:prysmaticlabs/prysm into kintsugi

* Update state_trie.go

* Clean up conflicts

* Fix build

* Update config to devnet1

* Fix state merge

* Handle merge test case for update balance

* Fix build

* State pkg cleanup

* Fix a bug with loading mainnet state

* Fix transactions root

* Add v2 endpoint for merge blocks (#9802)

* Add V2 blocks endpoint for merge blocks

* Update beacon-chain/rpc/apimiddleware/structs.go

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

* go mod

* fix transactions

* Terence's comments

* add missing file

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

* Sync

* Go mod tidy

* change EP field names

* latest kintusgi execution api

* fix conflicts

* converting base fee to big endian format (#10018)

* ReverseByteOrder function does not mess the input

* sync with develop

* use merge gossip sizes

* correct gossip sizes this time

* visibility

* clean ups

* Sync with develop, fix payload nil check bug

* Speed up syncing, hide cosmetic errors

* Sync with develop

* Clean up after sync

* Update generate_keys.go

* sync with develop

* Update mainnet_config.go

* Clean ups

* Sync optimistically candidate blocks (#10193)

* Revert "Sync optimistically candidate blocks (#10193)"

This reverts commit f99a0419ef.

* Sync optimistically candidate blocks (#10193)

* allow optimistic sync

* Fix merge transition block validation

* Update proposer.go

* Sync with develop

* delete deprecated client, update testnet flag

* Change optimistic logic (#10194)

* Logs and err handling

* Fix build

* Clean ups

* Add back get payload

* c

* Done

* Rm uncommented

* Optimistic sync: prysm validator rpcs (#10200)

* Logs to reproduce

* Use pointers

* Use pointers

* Use pointers

* Update json_marshal_unmarshal.go

* Fix marshal

* Update json_marshal_unmarshal.go

* Log

* string total diff

* str

* marshal un

* set string

* json

* gaz

* Comment out optimistic status

* remove kiln flag here (#10269)

* Sync with devleop

* Sync with develop

* clean ups

* refactor engine calls

* Update process_block.go

* Fix deadlock, uncomment duty opt sync

* Update proposer_execution_payload.go

* Sync with develop

* Rm post state check

* Bypass eth1 data checks

* Update proposer_execution_payload.go

* Return early if ttd is not reached

* Sync with devleop

* Update process_block.go

* Update receive_block.go

* Update bzl

* Revert "Update receive_block.go"

This reverts commit 5b4a87c512.

* Fix run time

* add in all the fixes

* fix evaluator bugs

* latest fixes

* sum

* fix to be configurable

* Update go.mod

* Fix AltairCompatible to account for future state version

* Update proposer_execution_payload.go

* fix broken conditional checks

* fix all issues

* Handle pre state Altair with valid payload

* Handle pre state Altair with valid payload

* Log bellatrix fields

* Update log.go

* Revert "fix broken conditional checks"

This reverts commit e118db6c20.

* LH multiclient working

* Friendly fee recipient log

* Remove extra SetOptimisticToValid

* fix race

* fix test

* Fix base fee per gas

* Fix notifypayload headroot

* tx fuzzer

* clean up with develop branch

* save progress

* 200tx/block

* add LH flags

* Sync with devleop

* cleanup

* cleanup

* hash

* fix build

* fix test

* fix go check

* fmt

* gosec

* Blocked stream

(cherry picked from commit f362af9862db680b6352692217ad5c08d44a1e86)

# Conflicts:
#	proto/prysm/v1alpha1/validator.pb.go

* remove duplicate param

* test

* revert some test changes

* Initial version of EE tx count

* evaluate all txs in epoch

* remove logs

* uncomment tests

* remove unwanted change

* parameterize ExpectedExecEngineTxsThreshold

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Zahoor Mohamed <zahoor@zahoor.in>
Co-authored-by: kasey <489222+kasey@users.noreply.github.com>
Co-authored-by: Potuz <potuz@prysmaticlabs.com>
Co-authored-by: Zahoor Mohamed <zahoor@prysmaticlabs.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-04-11 13:45:22 +00:00
terence tsao
d98428dec4 Can prune nodes from canonical and payload maps (#10496)
* Can prune nodes from canonical and payload maps

* Update store_test.go

* prune payload hashes, canonical nodes and better testing

Co-authored-by: Potuz <potuz@prysmaticlabs.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
2022-04-11 11:08:50 +00:00
Nishant Das
00b92e01d3 Fetch Non Finalized Deposits Better (#10505) 2022-04-11 09:59:22 +02:00
Potuz
ca5adbf7e4 Fix optimistic logging (#10503) 2022-04-09 23:09:04 +00:00
Nishant Das
a083b7a0a5 Set Auth Differently In Our Powchain Constructor (#10501) 2022-04-09 11:03:05 +02:00
Raul Jordan
dd5995b665 Proper Connection Management for ETH JSON-RPC Client for Startup and Runtime (#10498)
* begin connection management revamp

* kiln runs

* retry

* reconnect

* add

* rpc connect fix

* remove logging

* logs

* retry

* default value for web3flag

* test pass

* comments

* ensure auth works
2022-04-09 09:28:40 +08:00
240 changed files with 6151 additions and 13120 deletions

View File

@@ -115,18 +115,19 @@ nogo(
"@org_golang_x_tools//go/analysis/passes/assign:go_default_library",
"@org_golang_x_tools//go/analysis/passes/inspect:go_default_library",
"@org_golang_x_tools//go/analysis/passes/asmdecl:go_default_library",
"//tools/analyzers/maligned:go_default_library",
"//tools/analyzers/comparesame:go_default_library",
"//tools/analyzers/cryptorand:go_default_library",
"//tools/analyzers/errcheck:go_default_library",
"//tools/analyzers/featureconfig:go_default_library",
"//tools/analyzers/comparesame:go_default_library",
"//tools/analyzers/shadowpredecl:go_default_library",
"//tools/analyzers/nop:go_default_library",
"//tools/analyzers/slicedirect:go_default_library",
"//tools/analyzers/interfacechecker:go_default_library",
"//tools/analyzers/gocognit:go_default_library",
"//tools/analyzers/ineffassign:go_default_library",
"//tools/analyzers/interfacechecker:go_default_library",
"//tools/analyzers/maligned:go_default_library",
"//tools/analyzers/nop:go_default_library",
"//tools/analyzers/properpermissions:go_default_library",
"//tools/analyzers/recursivelock:go_default_library",
"//tools/analyzers/shadowpredecl:go_default_library",
"//tools/analyzers/slicedirect:go_default_library",
"//tools/analyzers/uintcast:go_default_library",
] + select({
# nogo checks that fail with coverage enabled.

View File

@@ -176,7 +176,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
go_rules_dependencies()
go_register_toolchains(
go_version = "1.17.6",
go_version = "1.17.9",
nogo = "@//:nogo",
)

View File

@@ -66,6 +66,7 @@ go_library(
"//config/params:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//monitoring/tracing:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/v1:go_default_library",

View File

@@ -273,13 +273,13 @@ func (s *Service) CurrentFork() *ethpb.Fork {
// IsCanonical returns true if the input block root is part of the canonical chain.
func (s *Service) IsCanonical(ctx context.Context, blockRoot [32]byte) (bool, error) {
// If the block has been finalized, the block will always be part of the canonical chain.
if s.cfg.BeaconDB.IsFinalizedBlock(ctx, blockRoot) {
return true, nil
// If the block has not been finalized, check fork choice store to see if the block is canonical
if s.cfg.ForkChoiceStore.HasNode(blockRoot) {
return s.cfg.ForkChoiceStore.IsCanonical(blockRoot), nil
}
// If the block has not been finalized, check fork choice store to see if the block is canonical
return s.cfg.ForkChoiceStore.IsCanonical(blockRoot), nil
// If the block has been finalized, the block will always be part of the canonical chain.
return s.cfg.BeaconDB.IsFinalizedBlock(ctx, blockRoot), nil
}
// ChainHeads returns all possible chain heads (leaves of fork choice tree).

View File

@@ -138,6 +138,26 @@ var (
Name: "state_balance_cache_miss",
Help: "Count the number of state balance cache hits.",
})
newPayloadValidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "new_payload_valid_node_count",
Help: "Count the number of valid nodes after newPayload EE call",
})
newPayloadOptimisticNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "new_payload_optimistic_node_count",
Help: "Count the number of optimistic nodes after newPayload EE call",
})
newPayloadInvalidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "new_payload_invalid_node_count",
Help: "Count the number of invalid nodes after newPayload EE call",
})
forkchoiceUpdatedValidNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "forkchoice_updated_valid_node_count",
Help: "Count the number of valid nodes after forkchoiceUpdated EE call",
})
forkchoiceUpdatedOptimisticNodeCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "forkchoice_updated_optimistic_node_count",
Help: "Count the number of optimistic nodes after forkchoiceUpdated EE call",
})
)
// reportSlotMetrics reports slot related metrics.

View File

@@ -83,16 +83,18 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, headState state.Be
if err != nil {
switch err {
case powchain.ErrAcceptedSyncingPayloadStatus:
forkchoiceUpdatedOptimisticNodeCount.Inc()
log.WithFields(logrus.Fields{
"headSlot": headBlk.Slot(),
"headHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash)),
"finalizedHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash)),
"headSlot": headBlk.Slot(),
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash)),
"finalizedPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash)),
}).Info("Called fork choice updated with optimistic block")
return payloadID, nil
default:
return nil, errors.Wrap(err, "could not notify forkchoice update from execution engine")
}
}
forkchoiceUpdatedValidNodeCount.Inc()
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, headRoot); err != nil {
return nil, errors.Wrap(err, "could not set block to valid")
}
@@ -135,12 +137,14 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion, postSta
if err != nil {
switch err {
case powchain.ErrAcceptedSyncingPayloadStatus:
newPayloadOptimisticNodeCount.Inc()
log.WithFields(logrus.Fields{
"slot": blk.Block().Slot(),
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
"slot": blk.Block().Slot(),
"payloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
}).Info("Called new payload with optimistic block")
return false, nil
case powchain.ErrInvalidPayloadStatus:
newPayloadInvalidNodeCount.Inc()
root, err := blk.Block().HashTreeRoot()
if err != nil {
return false, err
@@ -157,7 +161,7 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion, postSta
return false, errors.Wrap(err, "could not validate execution payload from execution engine")
}
}
newPayloadValidNodeCount.Inc()
// During the transition event, the transition block should be verified for sanity.
if blocks.IsPreBellatrixVersion(preStateVersion) {
// Handle case where pre-state is Altair but block contains payload.

View File

@@ -127,8 +127,8 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
}
}
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
return err
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, signed.Block(), blockRoot, postState); err != nil {
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
}
if isValidPayload {
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
@@ -148,6 +148,9 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b
return err
}
if err := s.savePostStateInfo(ctx, blockRoot, signed, postState, false /* reg sync */); err != nil {
return err
}
// If slasher is configured, forward the attestations in the block via
// an event feed for processing.
if features.Get().EnableSlasher {
@@ -609,9 +612,6 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b block.Sig
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
return errors.Wrap(err, "could not save state")
}
if err := s.insertBlockAndAttestationsToForkChoiceStore(ctx, b.Block(), r, st); err != nil {
return errors.Wrapf(err, "could not insert block %d to fork choice store", b.Block().Slot())
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
mathutil "github.com/prysmaticlabs/prysm/math"
"github.com/prysmaticlabs/prysm/monitoring/tracing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
@@ -402,10 +403,13 @@ func (s *Service) insertFinalizedDeposits(ctx context.Context, fRoot [32]byte) e
// We update the cache up to the last deposit index in the finalized block's state.
// We can be confident that these deposits will be included in some block
// because the Eth1 follow distance makes such long-range reorgs extremely unlikely.
eth1DepositIndex := int64(finalizedState.Eth1Data().DepositCount - 1)
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, eth1DepositIndex)
eth1DepositIndex, err := mathutil.Int(finalizedState.Eth1DepositIndex())
if err != nil {
return errors.Wrap(err, "could not cast eth1 deposit index")
}
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, int64(eth1DepositIndex))
// Deposit proofs are only used during state transition and can be safely removed to save space.
if err = s.cfg.DepositCache.PruneProofs(ctx, eth1DepositIndex); err != nil {
if err = s.cfg.DepositCache.PruneProofs(ctx, int64(eth1DepositIndex)); err != nil {
return errors.Wrap(err, "could not prune deposit proofs")
}
return nil

View File

@@ -1516,6 +1516,7 @@ func TestInsertFinalizedDeposits(t *testing.T) {
service.store.SetFinalizedCheckpt(&ethpb.Checkpoint{Root: gRoot[:]})
gs = gs.Copy()
assert.NoError(t, gs.SetEth1Data(&ethpb.Eth1Data{DepositCount: 10}))
assert.NoError(t, gs.SetEth1DepositIndex(7))
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k'}, gs))
zeroSig := [96]byte{}
for i := uint64(0); i < uint64(4*params.BeaconConfig().SlotsPerEpoch); i++ {
@@ -1529,8 +1530,64 @@ func TestInsertFinalizedDeposits(t *testing.T) {
}
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k'}))
fDeposits := depositCache.FinalizedDeposits(ctx)
assert.Equal(t, 9, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
deps := depositCache.AllDeposits(ctx, big.NewInt(109))
assert.Equal(t, 7, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
deps := depositCache.AllDeposits(ctx, big.NewInt(107))
for _, d := range deps {
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
}
}
func TestInsertFinalizedDeposits_MultipleFinalizedRoutines(t *testing.T) {
ctx := context.Background()
opts := testServiceOptsWithDB(t)
depositCache, err := depositcache.New()
require.NoError(t, err)
opts = append(opts, WithDepositCache(depositCache))
service, err := NewService(ctx, opts...)
require.NoError(t, err)
gs, _ := util.DeterministicGenesisState(t, 32)
require.NoError(t, service.saveGenesisData(ctx, gs))
gBlk, err := service.cfg.BeaconDB.GenesisBlock(ctx)
require.NoError(t, err)
gRoot, err := gBlk.Block().HashTreeRoot()
require.NoError(t, err)
service.store.SetFinalizedCheckpt(&ethpb.Checkpoint{Root: gRoot[:]})
gs = gs.Copy()
assert.NoError(t, gs.SetEth1Data(&ethpb.Eth1Data{DepositCount: 7}))
assert.NoError(t, gs.SetEth1DepositIndex(5))
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k'}, gs))
gs2 := gs.Copy()
assert.NoError(t, gs2.SetEth1Data(&ethpb.Eth1Data{DepositCount: 15}))
assert.NoError(t, gs2.SetEth1DepositIndex(12))
assert.NoError(t, service.cfg.StateGen.SaveState(ctx, [32]byte{'m', 'o', 'c', 'k', '2'}, gs2))
zeroSig := [96]byte{}
for i := uint64(0); i < uint64(4*params.BeaconConfig().SlotsPerEpoch); i++ {
root := []byte(strconv.Itoa(int(i)))
assert.NoError(t, depositCache.InsertDeposit(ctx, &ethpb.Deposit{Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.FromBytes48([fieldparams.BLSPubkeyLength]byte{}),
WithdrawalCredentials: params.BeaconConfig().ZeroHash[:],
Amount: 0,
Signature: zeroSig[:],
}, Proof: [][]byte{root}}, 100+i, int64(i), bytesutil.ToBytes32(root)))
}
// Insert 3 deposits before hand.
depositCache.InsertFinalizedDeposits(ctx, 2)
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k'}))
fDeposits := depositCache.FinalizedDeposits(ctx)
assert.Equal(t, 5, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
deps := depositCache.AllDeposits(ctx, big.NewInt(105))
for _, d := range deps {
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
}
// Insert New Finalized State with higher deposit count.
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k', '2'}))
fDeposits = depositCache.FinalizedDeposits(ctx)
assert.Equal(t, 12, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
deps = depositCache.AllDeposits(ctx, big.NewInt(112))
for _, d := range deps {
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
}

View File

@@ -83,7 +83,8 @@ func (s *Service) VerifyLmdFfgConsistency(ctx context.Context, a *ethpb.Attestat
func (s *Service) VerifyFinalizedConsistency(ctx context.Context, root []byte) error {
// A canonical root implies the root to has an ancestor that aligns with finalized check point.
// In this case, we could exit early to save on additional computation.
if s.cfg.ForkChoiceStore.IsCanonical(bytesutil.ToBytes32(root)) {
blockRoot := bytesutil.ToBytes32(root)
if s.cfg.ForkChoiceStore.HasNode(blockRoot) && s.cfg.ForkChoiceStore.IsCanonical(blockRoot) {
return nil
}

View File

@@ -36,7 +36,7 @@ type DepositFetcher interface {
DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int)
DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight *big.Int) (uint64, [32]byte)
FinalizedDeposits(ctx context.Context) *FinalizedDeposits
NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit
NonFinalizedDeposits(ctx context.Context, lastFinalizedIndex int64, untilBlk *big.Int) []*ethpb.Deposit
}
// FinalizedDeposits stores the trie of deposits that have been included
@@ -137,6 +137,22 @@ func (dc *DepositCache) InsertFinalizedDeposits(ctx context.Context, eth1Deposit
depositTrie := dc.finalizedDeposits.Deposits
insertIndex := int(dc.finalizedDeposits.MerkleTrieIndex + 1)
// Don't insert into finalized trie if there is no deposit to
// insert.
if len(dc.deposits) == 0 {
return
}
// In the event we have less deposits than we need to
// finalize we finalize till the index on which we do have it.
if len(dc.deposits) <= int(eth1DepositIndex) {
eth1DepositIndex = int64(len(dc.deposits)) - 1
}
// If we finalize to some lower deposit index, we
// ignore it.
if int(eth1DepositIndex) < insertIndex {
return
}
for _, d := range dc.deposits {
if d.Index <= dc.finalizedDeposits.MerkleTrieIndex {
continue
@@ -246,7 +262,7 @@ func (dc *DepositCache) FinalizedDeposits(ctx context.Context) *FinalizedDeposit
// NonFinalizedDeposits returns the list of non-finalized deposits until the given block number (inclusive).
// If no block is specified then this method returns all non-finalized deposits.
func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit {
func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, lastFinalizedIndex int64, untilBlk *big.Int) []*ethpb.Deposit {
ctx, span := trace.StartSpan(ctx, "DepositsCache.NonFinalizedDeposits")
defer span.End()
dc.depositsLock.RLock()
@@ -256,10 +272,9 @@ func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big.
return dc.allDeposits(untilBlk)
}
lastFinalizedDepositIndex := dc.finalizedDeposits.MerkleTrieIndex
var deposits []*ethpb.Deposit
for _, d := range dc.deposits {
if (d.Index > lastFinalizedDepositIndex) && (untilBlk == nil || untilBlk.Uint64() >= d.Eth1BlockHeight) {
if (d.Index > lastFinalizedIndex) && (untilBlk == nil || untilBlk.Uint64() >= d.Eth1BlockHeight) {
deposits = append(deposits, d.Deposit)
}
}

View File

@@ -459,7 +459,7 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
Index: 1,
},
}
newFinalizedDeposit := ethpb.DepositContainer{
newFinalizedDeposit := &ethpb.DepositContainer{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{2}, 48),
@@ -471,17 +471,17 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
}
dc.deposits = oldFinalizedDeposits
dc.InsertFinalizedDeposits(context.Background(), 1)
// Artificially exclude old deposits so that they can only be retrieved from previously finalized deposits.
dc.deposits = []*ethpb.DepositContainer{&newFinalizedDeposit}
dc.InsertFinalizedDeposits(context.Background(), 2)
dc.deposits = append(dc.deposits, []*ethpb.DepositContainer{newFinalizedDeposit}...)
cachedDeposits := dc.FinalizedDeposits(context.Background())
require.NotNil(t, cachedDeposits, "Deposits not cached")
assert.Equal(t, int64(2), cachedDeposits.MerkleTrieIndex)
assert.Equal(t, int64(1), cachedDeposits.MerkleTrieIndex)
var deps [][]byte
for _, d := range append(oldFinalizedDeposits, &newFinalizedDeposit) {
for _, d := range oldFinalizedDeposits {
hash, err := d.Deposit.Data.HashTreeRoot()
require.NoError(t, err, "Could not hash deposit data")
deps = append(deps, hash[:])
@@ -491,6 +491,140 @@ func TestFinalizedDeposits_UtilizesPreviouslyCachedDeposits(t *testing.T) {
assert.Equal(t, trie.HashTreeRoot(), cachedDeposits.Deposits.HashTreeRoot())
}
func TestFinalizedDeposits_HandleZeroDeposits(t *testing.T) {
dc, err := New()
require.NoError(t, err)
dc.InsertFinalizedDeposits(context.Background(), 2)
cachedDeposits := dc.FinalizedDeposits(context.Background())
require.NotNil(t, cachedDeposits, "Deposits not cached")
assert.Equal(t, int64(-1), cachedDeposits.MerkleTrieIndex)
}
func TestFinalizedDeposits_HandleSmallerThanExpectedDeposits(t *testing.T) {
dc, err := New()
require.NoError(t, err)
finalizedDeposits := []*ethpb.DepositContainer{
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{0}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 0,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{1}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 1,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{2}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 2,
},
}
dc.deposits = finalizedDeposits
dc.InsertFinalizedDeposits(context.Background(), 5)
cachedDeposits := dc.FinalizedDeposits(context.Background())
require.NotNil(t, cachedDeposits, "Deposits not cached")
assert.Equal(t, int64(2), cachedDeposits.MerkleTrieIndex)
}
func TestFinalizedDeposits_HandleLowerEth1DepositIndex(t *testing.T) {
dc, err := New()
require.NoError(t, err)
finalizedDeposits := []*ethpb.DepositContainer{
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{0}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 0,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{1}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 1,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{2}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 2,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{3}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 3,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{4}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 4,
},
{
Deposit: &ethpb.Deposit{
Data: &ethpb.Deposit_Data{
PublicKey: bytesutil.PadTo([]byte{5}, 48),
WithdrawalCredentials: make([]byte, 32),
Signature: make([]byte, 96),
},
},
Index: 5,
},
}
dc.deposits = finalizedDeposits
dc.InsertFinalizedDeposits(context.Background(), 5)
// Reinsert finalized deposits with a lower index.
dc.InsertFinalizedDeposits(context.Background(), 2)
cachedDeposits := dc.FinalizedDeposits(context.Background())
require.NotNil(t, cachedDeposits, "Deposits not cached")
assert.Equal(t, int64(5), cachedDeposits.MerkleTrieIndex)
}
func TestFinalizedDeposits_InitializedCorrectly(t *testing.T) {
dc, err := New()
require.NoError(t, err)
@@ -554,7 +688,7 @@ func TestNonFinalizedDeposits_ReturnsAllNonFinalizedDeposits(t *testing.T) {
})
dc.InsertFinalizedDeposits(context.Background(), 1)
deps := dc.NonFinalizedDeposits(context.Background(), nil)
deps := dc.NonFinalizedDeposits(context.Background(), 1, nil)
assert.Equal(t, 2, len(deps))
}
@@ -611,7 +745,7 @@ func TestNonFinalizedDeposits_ReturnsNonFinalizedDepositsUpToBlockNumber(t *test
})
dc.InsertFinalizedDeposits(context.Background(), 1)
deps := dc.NonFinalizedDeposits(context.Background(), big.NewInt(10))
deps := dc.NonFinalizedDeposits(context.Background(), 1, big.NewInt(10))
assert.Equal(t, 1, len(deps))
}

View File

@@ -155,7 +155,7 @@ func (_ *Service) FinalizedDeposits(_ context.Context) *depositcache.FinalizedDe
}
// NonFinalizedDeposits mocks out the deposit cache functionality for interop.
func (_ *Service) NonFinalizedDeposits(_ context.Context, _ *big.Int) []*ethpb.Deposit {
func (_ *Service) NonFinalizedDeposits(_ context.Context, _ int64, _ *big.Int) []*ethpb.Deposit {
return []*ethpb.Deposit{}
}

View File

@@ -51,10 +51,4 @@ var (
Help: "The number of times pruning happened.",
},
)
validatedCount = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "doublylinkedtree_validated_count",
Help: "The number of blocks that have been fully validated.",
},
)
)

View File

@@ -120,7 +120,6 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
}
n.optimistic = false
validatedCount.Inc()
return n.parent.setNodeAndParentValidated(ctx)
}

View File

@@ -51,10 +51,4 @@ var (
Help: "The number of times pruning happened.",
},
)
validatedNodesCount = promauto.NewCounter(
prometheus.CounterOpts{
Name: "proto_array_validated_nodes_count",
Help: "The number of nodes that have been fully validated.",
},
)
)

View File

@@ -43,7 +43,6 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [32]byte) er
if index == NonExistentNode {
break
}
validatedNodesCount.Inc()
}
return nil
}

View File

@@ -614,16 +614,22 @@ func (s *Store) prune(ctx context.Context, finalizedRoot [32]byte) error {
node := copyNode(s.nodes[idx])
parentIdx, ok := canonicalNodesMap[node.parent]
if ok {
s.nodesIndices[node.root] = uint64(len(canonicalNodes))
canonicalNodesMap[idx] = uint64(len(canonicalNodes))
currentIndex := uint64(len(canonicalNodes))
s.nodesIndices[node.root] = currentIndex
s.payloadIndices[node.payloadHash] = currentIndex
canonicalNodesMap[idx] = currentIndex
node.parent = parentIdx
canonicalNodes = append(canonicalNodes, node)
} else {
// Remove node and synced tip that is not part of finalized branch.
// Remove node that is not part of finalized branch.
delete(s.nodesIndices, node.root)
delete(s.canonicalNodes, node.root)
delete(s.payloadIndices, node.payloadHash)
}
}
s.nodesIndices[finalizedRoot] = uint64(0)
s.canonicalNodes[finalizedRoot] = true
s.payloadIndices[finalizedNode.payloadHash] = uint64(0)
// Recompute the best child and descendant for each canonical nodes.
for _, node := range canonicalNodes {

View File

@@ -375,7 +375,7 @@ func TestStore_Prune_MoreThanThreshold(t *testing.T) {
parent: uint64(numOfNodes - 2),
})
indices[indexToHash(uint64(numOfNodes-1))] = uint64(numOfNodes - 1)
s := &Store{nodes: nodes, nodesIndices: indices}
s := &Store{nodes: nodes, nodesIndices: indices, canonicalNodes: map[[32]byte]bool{}, payloadIndices: map[[32]byte]uint64{}}
// Finalized root is at index 99 so everything before 99 should be pruned.
require.NoError(t, s.prune(context.Background(), indexToHash(99)))
@@ -413,7 +413,7 @@ func TestStore_Prune_MoreThanOnce(t *testing.T) {
parent: uint64(numOfNodes - 2),
})
s := &Store{nodes: nodes, nodesIndices: indices}
s := &Store{nodes: nodes, nodesIndices: indices, canonicalNodes: map[[32]byte]bool{}, payloadIndices: map[[32]byte]uint64{}}
// Finalized root is at index 11 so everything before 11 should be pruned.
require.NoError(t, s.prune(context.Background(), indexToHash(10)))
@@ -441,6 +441,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
bestDescendant: 1,
root: indexToHash(uint64(0)),
parent: NonExistentNode,
payloadHash: [32]byte{'A'},
},
{
slot: 101,
@@ -448,6 +449,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
bestChild: NonExistentNode,
bestDescendant: NonExistentNode,
parent: 0,
payloadHash: [32]byte{'B'},
},
{
slot: 101,
@@ -455,6 +457,7 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
parent: 0,
bestChild: NonExistentNode,
bestDescendant: NonExistentNode,
payloadHash: [32]byte{'C'},
},
}
s := &Store{
@@ -465,9 +468,22 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
indexToHash(uint64(1)): 1,
indexToHash(uint64(2)): 2,
},
canonicalNodes: map[[32]byte]bool{
indexToHash(uint64(0)): true,
indexToHash(uint64(1)): true,
indexToHash(uint64(2)): true,
},
payloadIndices: map[[32]byte]uint64{
[32]byte{'A'}: 0,
[32]byte{'B'}: 1,
[32]byte{'C'}: 2,
},
}
require.NoError(t, s.prune(context.Background(), indexToHash(uint64(1))))
require.Equal(t, len(s.nodes), 1)
require.Equal(t, 1, len(s.nodes))
require.Equal(t, 1, len(s.nodesIndices))
require.Equal(t, 1, len(s.canonicalNodes))
require.Equal(t, 1, len(s.payloadIndices))
}
// This test starts with the following branching diagram
@@ -482,25 +498,74 @@ func TestStore_Prune_NoDanglingBranch(t *testing.T) {
// J -- K -- L
//
//
func TestStore_PruneSyncedTips(t *testing.T) {
func TestStore_PruneBranched(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, params.BeaconConfig().ZeroHash, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, params.BeaconConfig().ZeroHash, 1, 1))
f.store.pruneThreshold = 0
require.NoError(t, f.Prune(ctx, [32]byte{'f'}))
require.Equal(t, 1, f.NodeCount())
tests := []struct {
finalizedRoot [32]byte
wantedCanonical [32]byte
wantedNonCanonical [32]byte
canonicalCount int
payloadHash [32]byte
payloadIndex uint64
nonExistentPayload [32]byte
}{
{
[32]byte{'f'},
[32]byte{'f'},
[32]byte{'a'},
1,
[32]byte{'F'},
0,
[32]byte{'H'},
},
{
[32]byte{'d'},
[32]byte{'e'},
[32]byte{'i'},
3,
[32]byte{'E'},
1,
[32]byte{'C'},
},
{
[32]byte{'b'},
[32]byte{'f'},
[32]byte{'h'},
5,
[32]byte{'D'},
3,
[32]byte{'A'},
},
}
for _, tc := range tests {
f := setup(1, 1)
require.NoError(t, f.InsertOptimisticBlock(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 102, [32]byte{'j'}, [32]byte{'b'}, [32]byte{'J'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 103, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'e'}, [32]byte{'d'}, [32]byte{'E'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 104, [32]byte{'g'}, [32]byte{'d'}, [32]byte{'G'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 105, [32]byte{'k'}, [32]byte{'g'}, [32]byte{'K'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'i'}, [32]byte{'h'}, [32]byte{'I'}, 1, 1))
require.NoError(t, f.InsertOptimisticBlock(ctx, 106, [32]byte{'l'}, [32]byte{'k'}, [32]byte{'L'}, 1, 1))
f.store.pruneThreshold = 0
require.NoError(t, f.store.updateCanonicalNodes(ctx, [32]byte{'f'}))
require.Equal(t, true, f.IsCanonical([32]byte{'a'}))
require.Equal(t, true, f.IsCanonical([32]byte{'f'}))
require.NoError(t, f.Prune(ctx, tc.finalizedRoot))
require.Equal(t, tc.canonicalCount, len(f.store.canonicalNodes))
require.Equal(t, true, f.IsCanonical(tc.wantedCanonical))
require.Equal(t, false, f.IsCanonical(tc.wantedNonCanonical))
require.Equal(t, tc.payloadIndex, f.store.payloadIndices[tc.payloadHash])
_, ok := f.store.payloadIndices[tc.nonExistentPayload]
require.Equal(t, false, ok)
}
}
func TestStore_LeadsToViableHead(t *testing.T) {

View File

@@ -3,7 +3,6 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"auth.go",
"block_cache.go",
"block_reader.go",
"check_transition_config.go",
@@ -16,6 +15,7 @@ go_library(
"options.go",
"prometheus.go",
"provider.go",
"rpc_connection.go",
"service.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/powchain",
@@ -59,7 +59,6 @@ go_library(
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_golang_jwt_jwt_v4//:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
@@ -74,7 +73,6 @@ go_test(
name = "go_default_test",
size = "medium",
srcs = [
"auth_test.go",
"block_cache_test.go",
"block_reader_test.go",
"check_transition_config_test.go",
@@ -125,7 +123,6 @@ go_test(
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_ethereum_go_ethereum//trie:go_default_library",
"@com_github_golang_jwt_jwt_v4//:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/network"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/sirupsen/logrus"
)
@@ -81,7 +82,7 @@ func (s *Service) checkTransitionConfiguration(
return
}
case tm := <-ticker.C:
ctx, cancel := context.WithDeadline(ctx, tm.Add(DefaultRPCHTTPTimeout))
ctx, cancel := context.WithDeadline(ctx, tm.Add(network.DefaultRPCHTTPTimeout))
err = s.ExchangeTransitionConfiguration(ctx, cfg)
s.handleExchangeConfigurationError(err)
if !hasTtdReached {
@@ -119,11 +120,16 @@ func (s *Service) handleExchangeConfigurationError(err error) {
// Logs the terminal total difficulty status.
func (s *Service) logTtdStatus(ctx context.Context, ttd *uint256.Int) (bool, error) {
latest, err := s.LatestExecutionBlock(ctx)
if err != nil {
switch {
case errors.Is(err, hexutil.ErrEmptyString):
return false, nil
case err != nil:
return false, err
}
if latest == nil {
case latest == nil:
return false, errors.New("latest block is nil")
case latest.TotalDifficulty == "":
return false, nil
default:
}
latestTtd, err := hexutil.DecodeBig(latest.TotalDifficulty)
if err != nil {

View File

@@ -190,6 +190,38 @@ func TestService_logTtdStatus(t *testing.T) {
require.Equal(t, false, reached)
}
func TestService_logTtdStatus_NotSyncedClient(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
resp := (*pb.ExecutionBlock)(nil) // Nil response when a client is not synced
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": resp,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{
cfg: &config{},
}
service.rpcClient = rpcClient
ttd := new(uint256.Int)
reached, err := service.logTtdStatus(context.Background(), ttd.SetUint64(24343))
require.NoError(t, err)
require.Equal(t, false, reached)
}
func emptyPayload() *pb.ExecutionPayload {
return &pb.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),

View File

@@ -10,9 +10,12 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -49,8 +52,8 @@ type EngineCaller interface {
ExchangeTransitionConfiguration(
ctx context.Context, cfg *pb.TransitionConfiguration,
) error
LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error)
ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error)
}
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
@@ -174,6 +177,78 @@ func (s *Service) ExchangeTransitionConfiguration(
return nil
}
// GetTerminalBlockHash returns the valid terminal block hash based on total difficulty.
//
// Spec code:
// def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
// # `pow_chain` abstractly represents all blocks in the PoW chain
// for block in pow_chain:
// parent = pow_chain[block.parent_hash]
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
// if block_reached_ttd and not parent_reached_ttd:
// return block
//
// return None
func (s *Service) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
ttd := new(big.Int)
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
if overflows {
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
}
blk, err := s.LatestExecutionBlock(ctx)
if err != nil {
return nil, false, errors.Wrap(err, "could not get latest execution block")
}
if blk == nil {
return nil, false, errors.New("latest execution block is nil")
}
for {
if ctx.Err() != nil {
return nil, false, ctx.Err()
}
currentTotalDifficulty, err := tDStringToUint256(blk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
parentHash := bytesutil.ToBytes32(blk.ParentHash)
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
return nil, false, nil
}
parentBlk, err := s.ExecutionBlockByHash(ctx, parentHash)
if err != nil {
return nil, false, errors.Wrap(err, "could not get parent execution block")
}
if parentBlk == nil {
return nil, false, errors.New("parent execution block is nil")
}
if blockReachedTTD {
parentTotalDifficulty, err := tDStringToUint256(parentBlk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
if !parentReachedTTD {
log.WithFields(logrus.Fields{
"number": blk.Number,
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash)),
"td": blk.TotalDifficulty,
"parentTd": parentBlk.TotalDifficulty,
"ttd": terminalTotalDifficulty,
}).Info("Retrieved terminal block hash")
return blk.Hash, true, nil
}
} else {
return nil, false, nil
}
blk = parentBlk
}
}
// LatestExecutionBlock fetches the latest execution engine block by calling
// eth_blockByNumber via JSON-RPC.
func (s *Service) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error) {
@@ -251,3 +326,15 @@ func isTimeout(e error) bool {
t, ok := e.(httpTimeoutError)
return ok && t.Timeout()
}
func tDStringToUint256(td string) (*uint256.Int, error) {
b, err := hexutil.DecodeBig(td)
if err != nil {
return nil, err
}
i, overflows := uint256.FromBig(b)
if overflows {
return nil, errors.New("total difficulty overflowed")
}
return i, nil
}

View File

@@ -418,6 +418,155 @@ func TestClient_HTTP(t *testing.T) {
})
}
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
tests := []struct {
name string
paramsTd string
currentPowBlock *pb.ExecutionBlock
parentPowBlock *pb.ExecutionBlock
errLatestExecutionBlk error
wantTerminalBlockHash []byte
wantExists bool
errString string
}{
{
name: "config td overflows",
paramsTd: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
errString: "could not convert terminal total difficulty to uint256",
},
{
name: "could not get latest execution block",
paramsTd: "1",
errLatestExecutionBlk: errors.New("blah"),
errString: "could not get latest execution block",
},
{
name: "nil latest execution block",
paramsTd: "1",
errString: "latest execution block is nil",
},
{
name: "current execution block invalid TD",
paramsTd: "1",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
TotalDifficulty: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
},
errString: "could not convert total difficulty to uint256",
},
{
name: "current execution block has zero hash parent",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: params.BeaconConfig().ZeroHash[:],
TotalDifficulty: "0x3",
},
},
{
name: "could not get parent block",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
errString: "could not get parent execution block",
},
{
name: "parent execution block invalid TD",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
TotalDifficulty: "1",
},
errString: "could not convert total difficulty to uint256",
},
{
name: "happy case",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
TotalDifficulty: "0x1",
},
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) {
cfg := params.BeaconConfig()
cfg.TerminalTotalDifficulty = tt.paramsTd
params.OverrideBeaconConfig(cfg)
var m map[[32]byte]*pb.ExecutionBlock
if tt.parentPowBlock != nil {
m = map[[32]byte]*pb.ExecutionBlock{
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
}
}
client := mocks.EngineClient{
ErrLatestExecBlock: tt.errLatestExecutionBlk,
ExecutionBlock: tt.currentPowBlock,
BlockByHashMap: m,
}
b, e, err := client.GetTerminalBlockHash(context.Background())
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
require.NoError(t, err)
require.DeepEqual(t, tt.wantExists, e)
require.DeepEqual(t, tt.wantTerminalBlockHash, b)
}
})
}
}
func Test_tDStringToUint256(t *testing.T) {
i, err := tDStringToUint256("0x0")
require.NoError(t, err)
require.DeepEqual(t, uint256.NewInt(0), i)
i, err = tDStringToUint256("0x10000")
require.NoError(t, err)
require.DeepEqual(t, uint256.NewInt(65536), i)
_, err = tDStringToUint256("100")
require.ErrorContains(t, "hex string without 0x prefix", err)
_, err = tDStringToUint256("0xzzzzzz")
require.ErrorContains(t, "invalid hex string", err)
_, err = tDStringToUint256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
require.ErrorContains(t, "hex number > 256 bits", err)
}
func TestExchangeTransitionConfiguration(t *testing.T) {
fix := fixtures()
ctx := context.Background()

View File

@@ -1,9 +1,6 @@
package powchain
import (
"net/http"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
@@ -11,11 +8,9 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/network"
"github.com/prysmaticlabs/prysm/network/authorization"
)
// DefaultRPCHTTPTimeout for HTTP requests via an RPC connection to an execution node.
const DefaultRPCHTTPTimeout = time.Second * 6
type Option func(s *Service) error
// WithHttpEndpoints deduplicates and parses http endpoints for the powchain service to use,
@@ -38,20 +33,29 @@ func WithHttpEndpoints(endpointStrings []string) Option {
}
}
// WithJWTSecret for authenticating the execution node JSON-RPC endpoint.
func WithJWTSecret(secret []byte) Option {
return func(c *Service) error {
// WithHttpEndpointsAndJWTSecret for authenticating the execution node JSON-RPC endpoint.
func WithHttpEndpointsAndJWTSecret(endpointStrings []string, secret []byte) Option {
return func(s *Service) error {
if len(secret) == 0 {
return nil
}
authTransport := &jwtTransport{
underlyingTransport: http.DefaultTransport,
jwtSecret: secret,
stringEndpoints := dedupEndpoints(endpointStrings)
endpoints := make([]network.Endpoint, len(stringEndpoints))
// Overwrite authorization type for all endpoints to be of a bearer
// type.
for i, e := range stringEndpoints {
hEndpoint := HttpEndpoint(e)
hEndpoint.Auth.Method = authorization.Bearer
hEndpoint.Auth.Value = string(secret)
endpoints[i] = hEndpoint
}
c.cfg.httpRPCClient = &http.Client{
Timeout: DefaultRPCHTTPTimeout,
Transport: authTransport,
// Select first http endpoint in the provided list.
var currEndpoint network.Endpoint
if len(endpointStrings) > 0 {
currEndpoint = endpoints[0]
}
s.cfg.httpEndpoints = endpoints
s.cfg.currHttpEndpoint = currEndpoint
return nil
}
}

View File

@@ -0,0 +1,176 @@
package powchain
import (
"context"
"fmt"
"net/url"
"time"
"github.com/ethereum/go-ethereum/ethclient"
gethRPC "github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
"github.com/prysmaticlabs/prysm/io/logs"
"github.com/prysmaticlabs/prysm/network"
"github.com/prysmaticlabs/prysm/network/authorization"
)
func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpoint network.Endpoint) error {
client, err := s.newRPCClientWithAuth(ctx, currEndpoint)
if err != nil {
return errors.Wrap(err, "could not dial execution node")
}
// Attach the clients to the service struct.
fetcher := ethclient.NewClient(client)
s.rpcClient = client
s.httpLogger = fetcher
s.eth1DataFetcher = fetcher
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher)
if err != nil {
client.Close()
return errors.Wrap(err, "could not initialize deposit contract caller")
}
s.depositContractCaller = depositContractCaller
// Ensure we have the correct chain and deposit IDs.
if err := ensureCorrectExecutionChain(ctx, fetcher); err != nil {
client.Close()
return errors.Wrap(err, "could not make initial request to verify execution chain ID")
}
s.updateConnectedETH1(true)
s.runError = nil
return nil
}
// Every N seconds, defined as a backoffPeriod, attempts to re-establish an execution client
// connection and if this does not work, we fallback to the next endpoint if defined.
func (s *Service) pollConnectionStatus(ctx context.Context) {
// Use a custom logger to only log errors
logCounter := 0
errorLogger := func(err error, msg string) {
if logCounter > logThreshold {
log.Errorf("%s: %v", msg, err)
logCounter = 0
}
logCounter++
}
ticker := time.NewTicker(backOffPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
log.Debugf("Trying to dial endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
if err := s.setupExecutionClientConnections(ctx, s.cfg.currHttpEndpoint); err != nil {
errorLogger(err, "Could not connect to execution client endpoint")
s.runError = err
s.fallbackToNextEndpoint()
}
case <-s.ctx.Done():
log.Debug("Received cancelled context,closing existing powchain service")
return
}
}
}
// Forces to retry an execution client connection.
func (s *Service) retryExecutionClientConnection(ctx context.Context, err error) {
s.runError = err
s.updateConnectedETH1(false)
// Back off for a while before redialing.
time.Sleep(backOffPeriod)
if err := s.setupExecutionClientConnections(ctx, s.cfg.currHttpEndpoint); err != nil {
s.runError = err
return
}
// Reset run error in the event of a successful connection.
s.runError = nil
}
// This performs a health check on our primary endpoint, and if it
// is ready to serve we connect to it again. This method is only
// relevant if we are on our backup endpoint.
func (s *Service) checkDefaultEndpoint(ctx context.Context) {
primaryEndpoint := s.cfg.httpEndpoints[0]
// Return early if we are running on our primary
// endpoint.
if s.cfg.currHttpEndpoint.Equals(primaryEndpoint) {
return
}
if err := s.setupExecutionClientConnections(ctx, primaryEndpoint); err != nil {
log.Debugf("Primary endpoint not ready: %v", err)
return
}
s.updateCurrHttpEndpoint(primaryEndpoint)
}
// This is an inefficient way to search for the next endpoint, but given N is
// expected to be small, it is fine to search this way.
func (s *Service) fallbackToNextEndpoint() {
currEndpoint := s.cfg.currHttpEndpoint
currIndex := 0
totalEndpoints := len(s.cfg.httpEndpoints)
for i, endpoint := range s.cfg.httpEndpoints {
if endpoint.Equals(currEndpoint) {
currIndex = i
break
}
}
nextIndex := currIndex + 1
if nextIndex >= totalEndpoints {
nextIndex = 0
}
s.updateCurrHttpEndpoint(s.cfg.httpEndpoints[nextIndex])
if nextIndex != currIndex {
log.Infof("Falling back to alternative endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
}
}
// Initializes an RPC connection with authentication headers.
func (s *Service) newRPCClientWithAuth(ctx context.Context, endpoint network.Endpoint) (*gethRPC.Client, error) {
// Need to handle ipc and http
var client *gethRPC.Client
u, err := url.Parse(endpoint.Url)
if err != nil {
return nil, err
}
switch u.Scheme {
case "http", "https":
client, err = gethRPC.DialHTTPWithClient(endpoint.Url, endpoint.HttpClient())
if err != nil {
return nil, err
}
case "":
client, err = gethRPC.DialIPC(ctx, endpoint.Url)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
}
if endpoint.Auth.Method != authorization.None {
header, err := endpoint.Auth.ToHeaderValue()
if err != nil {
return nil, err
}
client.SetHeader("Authorization", header)
}
return client, nil
}
// Checks the chain ID of the execution client to ensure
// it matches local parameters of what Prysm expects.
func ensureCorrectExecutionChain(ctx context.Context, client *ethclient.Client) error {
cID, err := client.ChainID(ctx)
if err != nil {
return err
}
wantChainID := params.BeaconConfig().DepositChainID
if cID.Uint64() != wantChainID {
return fmt.Errorf("wanted chain ID %d, got %d", wantChainID, cID.Uint64())
}
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"context"
"fmt"
"math/big"
"net/http"
"reflect"
"runtime/debug"
"sort"
@@ -39,10 +38,8 @@ import (
"github.com/prysmaticlabs/prysm/container/trie"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/io/logs"
"github.com/prysmaticlabs/prysm/monitoring/clientstats"
"github.com/prysmaticlabs/prysm/network"
"github.com/prysmaticlabs/prysm/network/authorization"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
prysmTime "github.com/prysmaticlabs/prysm/time"
"github.com/prysmaticlabs/prysm/time/slots"
@@ -114,6 +111,7 @@ type Chain interface {
// RPCDataFetcher defines a subset of methods conformed to by ETH1.0 RPC clients for
// fetching eth1 data from the clients.
type RPCDataFetcher interface {
Close()
HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error)
HeaderByHash(ctx context.Context, hash common.Hash) (*gethTypes.Header, error)
SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error)
@@ -121,6 +119,7 @@ type RPCDataFetcher interface {
// RPCClient defines the rpc methods required to interact with the eth1 node.
type RPCClient interface {
Close()
BatchCall(b []gethRPC.BatchElem) error
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
}
@@ -135,7 +134,6 @@ type config struct {
eth1HeaderReqLimit uint64
beaconNodeStatsUpdater BeaconNodeStatsUpdater
httpEndpoints []network.Endpoint
httpRPCClient *http.Client
currHttpEndpoint network.Endpoint
finalizedStateAtStartup state.BeaconState
}
@@ -228,14 +226,9 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
// Start a web3 service's main event loop.
func (s *Service) Start() {
if err := s.connectToPowChain(); err != nil {
log.WithError(err).Fatal("Could not connect to execution endpoint")
if err := s.setupExecutionClientConnections(s.ctx, s.cfg.currHttpEndpoint); err != nil {
log.WithError(err).Error("Could not connect to execution endpoint")
}
log.WithFields(logrus.Fields{
"endpoint": logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url),
}).Info("Connected to Ethereum execution client RPC")
// If the chain has not started already and we don't have access to eth1 nodes, we will not be
// able to generate the genesis state.
if !s.chainStartData.Chainstarted && s.cfg.currHttpEndpoint.Url == "" {
@@ -253,7 +246,7 @@ func (s *Service) Start() {
s.isRunning = true
// Poll the execution client connection and fallback if errors occur.
go s.pollConnectionStatus()
go s.pollConnectionStatus(s.ctx)
// Check transition configuration for the engine API client in the background.
go s.checkTransitionConfiguration(s.ctx, make(chan *feed.Event, 1))
@@ -266,7 +259,12 @@ func (s *Service) Stop() error {
if s.cancel != nil {
defer s.cancel()
}
s.closeClients()
if s.rpcClient != nil {
s.rpcClient.Close()
}
if s.eth1DataFetcher != nil {
s.eth1DataFetcher.Close()
}
return nil
}
@@ -338,10 +336,7 @@ func (s *Service) CurrentETH1Endpoint() string {
// CurrentETH1ConnectionError returns the error (if any) of the current connection.
func (s *Service) CurrentETH1ConnectionError() error {
httpClient, rpcClient, err := s.dialETH1Nodes(s.cfg.currHttpEndpoint)
httpClient.Close()
rpcClient.Close()
return err
return s.runError
}
// ETH1Endpoints returns the slice of HTTP endpoint URLs (default is 0th element).
@@ -358,10 +353,17 @@ func (s *Service) ETH1Endpoints() []string {
func (s *Service) ETH1ConnectionErrors() []error {
var errs []error
for _, ep := range s.cfg.httpEndpoints {
httpClient, rpcClient, err := s.dialETH1Nodes(ep)
httpClient.Close()
rpcClient.Close()
errs = append(errs, err)
client, err := s.newRPCClientWithAuth(s.ctx, ep)
if err != nil {
errs = append(errs, err)
continue
}
if err := ensureCorrectExecutionChain(s.ctx, ethclient.NewClient(client)); err != nil {
client.Close()
errs = append(errs, err)
continue
}
client.Close()
}
return errs
}
@@ -376,146 +378,6 @@ func (s *Service) followBlockHeight(_ context.Context) (uint64, error) {
return latestValidBlock, nil
}
func (s *Service) connectToPowChain() error {
httpClient, rpcClient, err := s.dialETH1Nodes(s.cfg.currHttpEndpoint)
if err != nil {
return errors.Wrap(err, "could not dial execution node")
}
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, httpClient)
if err != nil {
return errors.Wrap(err, "could not initialize deposit contract caller")
}
if httpClient == nil || rpcClient == nil || depositContractCaller == nil {
return errors.New("execution client RPC is nil")
}
s.httpLogger = httpClient
s.eth1DataFetcher = httpClient
s.depositContractCaller = depositContractCaller
s.rpcClient = rpcClient
s.updateConnectedETH1(true)
s.runError = nil
return nil
}
func (s *Service) dialETH1Nodes(endpoint network.Endpoint) (*ethclient.Client, *gethRPC.Client, error) {
httpRPCClient, err := gethRPC.Dial(endpoint.Url)
if err != nil {
return nil, nil, err
}
if endpoint.Auth.Method != authorization.None {
header, err := endpoint.Auth.ToHeaderValue()
if err != nil {
return nil, nil, err
}
httpRPCClient.SetHeader("Authorization", header)
}
httpClient := ethclient.NewClient(httpRPCClient)
// Add a method to clean-up and close clients in the event
// of any connection failure.
closeClients := func() {
httpRPCClient.Close()
httpClient.Close()
}
// Make a simple call to ensure we are actually connected to a working node.
cID, err := httpClient.ChainID(s.ctx)
if err != nil {
closeClients()
return nil, nil, err
}
nID, err := httpClient.NetworkID(s.ctx)
if err != nil {
closeClients()
return nil, nil, err
}
if cID.Uint64() != params.BeaconConfig().DepositChainID {
closeClients()
return nil, nil, fmt.Errorf("eth1 node using incorrect chain id, %d != %d", cID.Uint64(), params.BeaconConfig().DepositChainID)
}
if nID.Uint64() != params.BeaconConfig().DepositNetworkID {
closeClients()
return nil, nil, fmt.Errorf("eth1 node using incorrect network id, %d != %d", nID.Uint64(), params.BeaconConfig().DepositNetworkID)
}
return httpClient, httpRPCClient, nil
}
// closes down our active eth1 clients.
func (s *Service) closeClients() {
gethClient, ok := s.rpcClient.(*gethRPC.Client)
if ok {
gethClient.Close()
}
httpClient, ok := s.eth1DataFetcher.(*ethclient.Client)
if ok {
httpClient.Close()
}
}
func (s *Service) pollConnectionStatus() {
// Use a custom logger to only log errors
logCounter := 0
errorLogger := func(err error, msg string) {
if logCounter > logThreshold {
log.Errorf("%s: %v", msg, err)
logCounter = 0
}
logCounter++
}
ticker := time.NewTicker(backOffPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
log.Debugf("Trying to dial endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
errConnect := s.connectToPowChain()
if errConnect != nil {
errorLogger(errConnect, "Could not connect to powchain endpoint")
s.runError = errConnect
s.fallbackToNextEndpoint()
continue
}
case <-s.ctx.Done():
log.Debug("Received cancelled context,closing existing powchain service")
return
}
}
}
// checks if the eth1 node is healthy and ready to serve before
// fetching data from it.
func (s *Service) isEth1NodeSynced() (bool, error) {
syncProg, err := s.eth1DataFetcher.SyncProgress(s.ctx)
if err != nil {
return false, err
}
if syncProg != nil {
return false, nil
}
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
if err != nil {
return false, err
}
return !eth1HeadIsBehind(head.Time), nil
}
// Reconnect to eth1 node in case of any failure.
func (s *Service) retryETH1Node(err error) {
s.runError = err
s.updateConnectedETH1(false)
// Back off for a while before
// resuming dialing the eth1 node.
time.Sleep(backOffPeriod)
if err := s.connectToPowChain(); err != nil {
s.runError = err
return
}
// Reset run error in the event of a successful connection.
s.runError = nil
}
func (s *Service) initDepositCaches(ctx context.Context, ctrs []*ethpb.DepositContainer) error {
if len(ctrs) == 0 {
return nil
@@ -650,7 +512,7 @@ func (s *Service) handleETH1FollowDistance() {
fiveMinutesTimeout := prysmTime.Now().Add(-5 * time.Minute)
// check that web3 client is syncing
if time.Unix(int64(s.latestEth1Data.BlockTime), 0).Before(fiveMinutesTimeout) {
log.Warn("eth1 client is not syncing")
log.Warn("Execution client is not syncing")
}
if !s.chainStartData.Chainstarted {
if err := s.checkBlockNumberForChainStart(ctx, big.NewInt(int64(s.latestEth1Data.LastRequestedBlock))); err != nil {
@@ -680,6 +542,15 @@ func (s *Service) handleETH1FollowDistance() {
}
func (s *Service) initPOWService() {
// Use a custom logger to only log errors
logCounter := 0
errorLogger := func(err error, msg string) {
if logCounter > logThreshold {
log.Errorf("%s: %v", msg, err)
logCounter = 0
}
logCounter++
}
// Run in a select loop to retry in the event of any failures.
for {
@@ -690,8 +561,8 @@ func (s *Service) initPOWService() {
ctx := s.ctx
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil)
if err != nil {
log.Errorf("Unable to retrieve latest ETH1.0 chain header: %v", err)
s.retryETH1Node(err)
s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to retrieve latest execution client header")
continue
}
@@ -700,14 +571,14 @@ func (s *Service) initPOWService() {
s.latestEth1Data.BlockTime = header.Time
if err := s.processPastLogs(ctx); err != nil {
log.Errorf("Unable to process past logs %v", err)
s.retryETH1Node(err)
s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to process past deposit contract logs")
continue
}
// Cache eth1 headers from our voting period.
if err := s.cacheHeadersForEth1DataVote(ctx); err != nil {
log.Errorf("Unable to process past headers %v", err)
s.retryETH1Node(err)
s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to cache headers for execution client votes")
continue
}
// Handle edge case with embedded genesis state by fetching genesis header to determine
@@ -720,15 +591,15 @@ func (s *Service) initPOWService() {
if genHash != [32]byte{} {
genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, genHash)
if err != nil {
log.Errorf("Unable to retrieve genesis ETH1.0 chain header: %v", err)
s.retryETH1Node(err)
s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to retrieve proof-of-stake genesis block data")
continue
}
genBlock = genHeader.Number.Uint64()
}
s.chainStartData.GenesisBlock = genBlock
if err := s.savePowchainData(ctx); err != nil {
log.Errorf("Unable to save powchain data: %v", err)
errorLogger(err, "Unable to save execution client data")
}
}
return
@@ -757,17 +628,16 @@ func (s *Service) run(done <-chan struct{}) {
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
if err != nil {
log.WithError(err).Debug("Could not fetch latest eth1 header")
s.retryETH1Node(err)
continue
}
if eth1HeadIsBehind(head.Time) {
s.retryExecutionClientConnection(s.ctx, err)
log.WithError(errFarBehind).Debug("Could not get an up to date eth1 header")
s.retryETH1Node(errFarBehind)
continue
}
s.processBlockHeader(head)
s.handleETH1FollowDistance()
s.checkDefaultEndpoint()
s.checkDefaultEndpoint(s.ctx)
case <-chainstartTicker.C:
if s.chainStartData.Chainstarted {
chainstartTicker.Stop()
@@ -854,59 +724,6 @@ func (s *Service) determineEarliestVotingBlock(ctx context.Context, followBlock
return hdr.Number.Uint64(), nil
}
// This performs a health check on our primary endpoint, and if it
// is ready to serve we connect to it again. This method is only
// relevant if we are on our backup endpoint.
func (s *Service) checkDefaultEndpoint() {
primaryEndpoint := s.cfg.httpEndpoints[0]
// Return early if we are running on our primary
// endpoint.
if s.cfg.currHttpEndpoint.Equals(primaryEndpoint) {
return
}
httpClient, rpcClient, err := s.dialETH1Nodes(primaryEndpoint)
if err != nil {
log.Debugf("Primary endpoint not ready: %v", err)
return
}
log.Info("Primary endpoint ready again, switching back to it")
// Close the clients and let our main connection routine
// properly connect with it.
httpClient.Close()
rpcClient.Close()
// Close current active clients.
s.closeClients()
// Switch back to primary endpoint and try connecting
// to it again.
s.updateCurrHttpEndpoint(primaryEndpoint)
s.retryETH1Node(nil)
}
// This is an inefficient way to search for the next endpoint, but given N is expected to be
// small ( < 25), it is fine to search this way.
func (s *Service) fallbackToNextEndpoint() {
currEndpoint := s.cfg.currHttpEndpoint
currIndex := 0
totalEndpoints := len(s.cfg.httpEndpoints)
for i, endpoint := range s.cfg.httpEndpoints {
if endpoint.Equals(currEndpoint) {
currIndex = i
break
}
}
nextIndex := currIndex + 1
if nextIndex >= totalEndpoints {
nextIndex = 0
}
s.updateCurrHttpEndpoint(s.cfg.httpEndpoints[nextIndex])
if nextIndex != currIndex {
log.Infof("Falling back to alternative endpoint: %s", logs.MaskCredentialsLogging(s.cfg.currHttpEndpoint.Url))
}
}
// initializes our service from the provided eth1data object by initializing all the relevant
// fields and data.
func (s *Service) initializeEth1Data(ctx context.Context, eth1DataInDB *ethpb.ETH1ChainData) error {

View File

@@ -42,6 +42,8 @@ type goodLogger struct {
backend *backends.SimulatedBackend
}
func (_ *goodLogger) Close() {}
func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
if g.backend == nil {
return new(event.Feed).Subscribe(ch), nil
@@ -80,6 +82,8 @@ type goodFetcher struct {
backend *backends.SimulatedBackend
}
func (_ *goodFetcher) Close() {}
func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) {
if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) {
return nil, fmt.Errorf("expected block hash to be nonzero %v", hash)
@@ -225,10 +229,6 @@ func TestService_Eth1Synced(t *testing.T) {
now := time.Now()
assert.NoError(t, testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0))))
testAcc.Backend.Commit()
synced, err := web3Service.isEth1NodeSynced()
require.NoError(t, err)
assert.Equal(t, true, synced, "Expected eth1 nodes to be synced")
}
func TestFollowBlock_OK(t *testing.T) {
@@ -480,8 +480,8 @@ func TestInitDepositCacheWithFinalization_OK(t *testing.T) {
s.chainStartData.Chainstarted = true
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
deps := s.cfg.depositCache.NonFinalizedDeposits(context.Background(), nil)
fDeposits := s.cfg.depositCache.FinalizedDeposits(ctx)
deps := s.cfg.depositCache.NonFinalizedDeposits(context.Background(), fDeposits.MerkleTrieIndex, nil)
assert.Equal(t, 0, len(deps))
}

View File

@@ -26,6 +26,7 @@ go_library(
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)

View File

@@ -2,25 +2,32 @@ package testing
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
)
// EngineClient --
type EngineClient struct {
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
TerminalBlockHash []byte
TerminalBlockHashExists bool
}
// NewPayload --
@@ -58,3 +65,52 @@ func (e *EngineClient) ExecutionBlockByHash(_ context.Context, h common.Hash) (*
}
return b, e.ErrExecBlockByHash
}
// GetTerminalBlockHash --
func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
ttd := new(big.Int)
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
if overflows {
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
}
blk, err := e.LatestExecutionBlock(ctx)
if err != nil {
return nil, false, errors.Wrap(err, "could not get latest execution block")
}
if blk == nil {
return nil, false, errors.New("latest execution block is nil")
}
for {
b, err := hexutil.DecodeBig(blk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
currentTotalDifficulty, _ := uint256.FromBig(b)
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
parentHash := bytesutil.ToBytes32(blk.ParentHash)
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
return nil, false, nil
}
parentBlk, err := e.ExecutionBlockByHash(ctx, parentHash)
if err != nil {
return nil, false, errors.Wrap(err, "could not get parent execution block")
}
if blockReachedTTD {
b, err := hexutil.DecodeBig(parentBlk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
parentTotalDifficulty, _ := uint256.FromBig(b)
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
if !parentReachedTTD {
return blk.Hash, true, nil
}
} else {
return nil, false, nil
}
blk = parentBlk
}
}

View File

@@ -144,6 +144,8 @@ type RPCClient struct {
Backend *backends.SimulatedBackend
}
func (_ *RPCClient) Close() {}
func (*RPCClient) CallContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
return nil
}

View File

@@ -77,7 +77,6 @@ go_library(
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ferranbt_fastssz//:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
@@ -162,7 +161,6 @@ go_test(
"@com_github_d4l3k_messagediff//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",

View File

@@ -37,17 +37,24 @@ func (vs *Server) StreamBlocksAltair(req *ethpb.StreamBlocksRequest, stream ethp
case version.Phase0:
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlock)
if !ok {
log.Warn("Mismatch between version and block type, was expecting *ethpb.SignedBeaconBlock")
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlock")
continue
}
b.Block = &ethpb.StreamBlocksResponse_Phase0Block{Phase0Block: phBlk}
case version.Altair:
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlockAltair)
if !ok {
log.Warn("Mismatch between version and block type, was expecting *v2.SignedBeaconBlockAltair")
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlockAltair")
continue
}
b.Block = &ethpb.StreamBlocksResponse_AltairBlock{AltairBlock: phBlk}
case version.Bellatrix:
phBlk, ok := data.SignedBlock.Proto().(*ethpb.SignedBeaconBlockBellatrix)
if !ok {
log.Warn("Mismatch between version and block type, was expecting SignedBeaconBlockBellatrix")
continue
}
b.Block = &ethpb.StreamBlocksResponse_BellatrixBlock{BellatrixBlock: phBlk}
}
if err := stream.Send(b); err != nil {

View File

@@ -161,7 +161,7 @@ func (vs *Server) depositTrie(ctx context.Context, canonicalEth1Data *ethpb.Eth1
finalizedDeposits := vs.DepositFetcher.FinalizedDeposits(ctx)
depositTrie = finalizedDeposits.Deposits
upToEth1DataDeposits := vs.DepositFetcher.NonFinalizedDeposits(ctx, canonicalEth1DataHeight)
upToEth1DataDeposits := vs.DepositFetcher.NonFinalizedDeposits(ctx, finalizedDeposits.MerkleTrieIndex, canonicalEth1DataHeight)
insertIndex := finalizedDeposits.MerkleTrieIndex + 1
for _, dep := range upToEth1DataDeposits {

View File

@@ -3,11 +3,7 @@ package validator
import (
"bytes"
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
@@ -181,79 +177,7 @@ func (vs *Server) getTerminalBlockHashIfExists(ctx context.Context) ([]byte, boo
return terminalBlockHash.Bytes(), true, nil
}
return vs.getPowBlockHashAtTerminalTotalDifficulty(ctx)
}
// This returns the valid terminal block hash based on total difficulty.
//
// Spec code:
// def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
// # `pow_chain` abstractly represents all blocks in the PoW chain
// for block in pow_chain:
// parent = pow_chain[block.parent_hash]
// block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
// parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
// if block_reached_ttd and not parent_reached_ttd:
// return block
//
// return None
func (vs *Server) getPowBlockHashAtTerminalTotalDifficulty(ctx context.Context) ([]byte, bool, error) {
ttd := new(big.Int)
ttd.SetString(params.BeaconConfig().TerminalTotalDifficulty, 10)
terminalTotalDifficulty, overflows := uint256.FromBig(ttd)
if overflows {
return nil, false, errors.New("could not convert terminal total difficulty to uint256")
}
blk, err := vs.ExecutionEngineCaller.LatestExecutionBlock(ctx)
if err != nil {
return nil, false, errors.Wrap(err, "could not get latest execution block")
}
if blk == nil {
return nil, false, errors.New("latest execution block is nil")
}
for {
if ctx.Err() != nil {
return nil, false, ctx.Err()
}
currentTotalDifficulty, err := tDStringToUint256(blk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
parentHash := bytesutil.ToBytes32(blk.ParentHash)
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
return nil, false, nil
}
parentBlk, err := vs.ExecutionEngineCaller.ExecutionBlockByHash(ctx, parentHash)
if err != nil {
return nil, false, errors.Wrap(err, "could not get parent execution block")
}
if parentBlk == nil {
return nil, false, errors.New("parent execution block is nil")
}
if blockReachedTTD {
parentTotalDifficulty, err := tDStringToUint256(parentBlk.TotalDifficulty)
if err != nil {
return nil, false, errors.Wrap(err, "could not convert total difficulty to uint256")
}
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
if !parentReachedTTD {
log.WithFields(logrus.Fields{
"number": blk.Number,
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash)),
"td": blk.TotalDifficulty,
"parentTd": parentBlk.TotalDifficulty,
"ttd": terminalTotalDifficulty,
}).Info("Retrieved terminal block hash")
return blk.Hash, true, nil
}
} else {
return nil, false, nil
}
blk = parentBlk
}
return vs.ExecutionEngineCaller.GetTerminalBlockHash(ctx)
}
// activationEpochNotReached returns true if activation epoch has not been reach.
@@ -270,18 +194,6 @@ func activationEpochNotReached(slot types.Slot) bool {
return false
}
func tDStringToUint256(td string) (*uint256.Int, error) {
b, err := hexutil.DecodeBig(td)
if err != nil {
return nil, err
}
i, overflows := uint256.FromBig(b)
if overflows {
return nil, errors.New("total difficulty overflowed")
}
return i, nil
}
func emptyPayload() *enginev1.ExecutionPayload {
return &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),

View File

@@ -6,7 +6,6 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
types "github.com/prysmaticlabs/eth2-types"
chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
@@ -22,26 +21,6 @@ import (
"github.com/prysmaticlabs/prysm/testing/util"
)
func Test_tDStringToUint256(t *testing.T) {
i, err := tDStringToUint256("0x0")
require.NoError(t, err)
require.DeepEqual(t, uint256.NewInt(0), i)
i, err = tDStringToUint256("0x10000")
require.NoError(t, err)
require.DeepEqual(t, uint256.NewInt(65536), i)
_, err = tDStringToUint256("100")
require.ErrorContains(t, "hex string without 0x prefix", err)
_, err = tDStringToUint256("0xzzzzzz")
require.ErrorContains(t, "invalid hex string", err)
_, err = tDStringToUint256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
require.ErrorContains(t, "hex number > 256 bits", err)
}
func TestServer_activationEpochNotReached(t *testing.T) {
require.Equal(t, false, activationEpochNotReached(0))
@@ -154,137 +133,6 @@ func TestServer_getExecutionPayload(t *testing.T) {
}
}
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
tests := []struct {
name string
paramsTd string
currentPowBlock *pb.ExecutionBlock
parentPowBlock *pb.ExecutionBlock
errLatestExecutionBlk error
wantTerminalBlockHash []byte
wantExists bool
errString string
}{
{
name: "config td overflows",
paramsTd: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
errString: "could not convert terminal total difficulty to uint256",
},
{
name: "could not get latest execution block",
paramsTd: "1",
errLatestExecutionBlk: errors.New("blah"),
errString: "could not get latest execution block",
},
{
name: "nil latest execution block",
paramsTd: "1",
errString: "latest execution block is nil",
},
{
name: "current execution block invalid TD",
paramsTd: "1",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
TotalDifficulty: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
},
errString: "could not convert total difficulty to uint256",
},
{
name: "current execution block has zero hash parent",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: params.BeaconConfig().ZeroHash[:],
TotalDifficulty: "0x3",
},
},
{
name: "could not get parent block",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
errString: "could not get parent execution block",
},
{
name: "parent execution block invalid TD",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
TotalDifficulty: "1",
},
errString: "could not convert total difficulty to uint256",
},
{
name: "happy case",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
TotalDifficulty: "0x1",
},
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) {
cfg := params.BeaconConfig()
cfg.TerminalTotalDifficulty = tt.paramsTd
params.OverrideBeaconConfig(cfg)
var m map[[32]byte]*pb.ExecutionBlock
if tt.parentPowBlock != nil {
m = map[[32]byte]*pb.ExecutionBlock{
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
}
}
vs := &Server{
ExecutionEngineCaller: &powtesting.EngineClient{
ErrLatestExecBlock: tt.errLatestExecutionBlk,
ExecutionBlock: tt.currentPowBlock,
BlockByHashMap: m,
},
}
b, e, err := vs.getPowBlockHashAtTerminalTotalDifficulty(context.Background())
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
require.NoError(t, err)
require.DeepEqual(t, tt.wantExists, e)
require.DeepEqual(t, tt.wantTerminalBlockHash, b)
}
})
}
}
func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
tests := []struct {
name string

View File

@@ -10,6 +10,7 @@ go_library(
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//beacon-chain/state/state-native/types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//crypto/hash:go_default_library",

View File

@@ -16,7 +16,7 @@ type FieldTrie struct {
*sync.RWMutex
reference *stateutil.Reference
fieldLayers [][]*[32]byte
field types.FieldIndex
field types.FieldIdx
dataType types.DataType
length uint64
numOfElems int
@@ -25,7 +25,7 @@ type FieldTrie struct {
// NewFieldTrie is the constructor for the field trie data structure. It creates the corresponding
// trie according to the given parameters. Depending on whether the field is a basic/composite array
// which is either fixed/variable length, it will appropriately determine the trie.
func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) {
func NewFieldTrie(field types.FieldIdx, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) {
if elements == nil {
return &FieldTrie{
field: field,

View File

@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/crypto/hash"
@@ -18,7 +19,7 @@ import (
)
// ProofFromMerkleLayers creates a proof starting at the leaf index of the state Merkle layers.
func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex types.FieldIndex) [][]byte {
func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex int) [][]byte {
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
proof := make([][]byte, 0)
@@ -49,7 +50,7 @@ func (f *FieldTrie) validateIndices(idxs []uint64) error {
return nil
}
func validateElements(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) error {
func validateElements(field types.FieldIdx, dataType types.DataType, elements interface{}, length uint64) error {
if dataType == types.CompressedArray {
comLength, err := field.ElemsInChunk()
if err != nil {
@@ -65,68 +66,117 @@ func validateElements(field types.FieldIndex, dataType types.DataType, elements
}
// fieldConverters converts the corresponding field and the provided elements to the appropriate roots.
func fieldConverters(field types.FieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
func fieldConverters(field types.FieldIdx, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
if field.Native() {
switch field {
case nativetypes.BlockRoots:
return convertBlockRoots(indices, elements, convertAll)
case nativetypes.StateRoots:
return convertStateRoots(indices, elements, convertAll)
case nativetypes.RandaoMixes:
return convertRandaoMixes(indices, elements, convertAll)
case nativetypes.Eth1DataVotes:
return convertEth1DataVotes(indices, elements, convertAll)
case nativetypes.Validators:
return convertValidators(indices, elements, convertAll)
case nativetypes.PreviousEpochAttestations, nativetypes.CurrentEpochAttestations:
return convertAttestations(indices, elements, convertAll)
case nativetypes.Balances:
return convertBalances(indices, elements, convertAll)
default:
return [][32]byte{}, errors.Errorf("got unsupported type of %v", reflect.TypeOf(elements).Name())
}
}
switch field {
case types.BlockRoots:
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.BlockRoots:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for block roots")
}
return convertBlockRoots(indices, elements, convertAll)
case types.StateRoots:
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.StateRoots:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for state roots")
}
return convertStateRoots(indices, elements, convertAll)
case types.RandaoMixes:
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.RandaoMixes:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for randao mixes")
}
return convertRandaoMixes(indices, elements, convertAll)
case types.Eth1DataVotes:
val, ok := elements.([]*ethpb.Eth1Data)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Eth1Data{}).Name(), reflect.TypeOf(elements).Name())
}
return handleEth1DataSlice(val, indices, convertAll)
return convertEth1DataVotes(indices, elements, convertAll)
case types.Validators:
val, ok := elements.([]*ethpb.Validator)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Validator{}).Name(), reflect.TypeOf(elements).Name())
}
return handleValidatorSlice(val, indices, convertAll)
return convertValidators(indices, elements, convertAll)
case types.PreviousEpochAttestations, types.CurrentEpochAttestations:
val, ok := elements.([]*ethpb.PendingAttestation)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.PendingAttestation{}).Name(), reflect.TypeOf(elements).Name())
}
return handlePendingAttestationSlice(val, indices, convertAll)
return convertAttestations(indices, elements, convertAll)
case types.Balances:
val, ok := elements.([]uint64)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name())
}
return handleBalanceSlice(val, indices, convertAll)
return convertBalances(indices, elements, convertAll)
default:
return [][32]byte{}, errors.Errorf("got unsupported type of %v", reflect.TypeOf(elements).Name())
}
}
func convertBlockRoots(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.BlockRoots:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for block roots")
}
}
func convertStateRoots(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.StateRoots:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for state roots")
}
}
func convertRandaoMixes(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
switch val := elements.(type) {
case [][]byte:
return handleByteArrays(val, indices, convertAll)
case *customtypes.RandaoMixes:
return handle32ByteArrays(val[:], indices, convertAll)
default:
return nil, errors.Errorf("Incorrect type used for randao mixes")
}
}
func convertEth1DataVotes(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
val, ok := elements.([]*ethpb.Eth1Data)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Eth1Data{}).Name(), reflect.TypeOf(elements).Name())
}
return handleEth1DataSlice(val, indices, convertAll)
}
func convertValidators(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
val, ok := elements.([]*ethpb.Validator)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.Validator{}).Name(), reflect.TypeOf(elements).Name())
}
return handleValidatorSlice(val, indices, convertAll)
}
func convertAttestations(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
val, ok := elements.([]*ethpb.PendingAttestation)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]*ethpb.PendingAttestation{}).Name(), reflect.TypeOf(elements).Name())
}
return handlePendingAttestationSlice(val, indices, convertAll)
}
func convertBalances(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
val, ok := elements.([]uint64)
if !ok {
return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name())
}
return handleBalanceSlice(val, indices, convertAll)
}
// handleByteArrays computes and returns byte arrays in a slice of root format.
func handleByteArrays(val [][]byte, indices []uint64, convertAll bool) ([][32]byte, error) {
length := len(indices)

View File

@@ -18,7 +18,7 @@ func TestFieldTrie_NewTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 40)
// 5 represents the enum value of state roots
trie, err := fieldtrie.NewFieldTrie(5, stateTypes.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
trie, err := fieldtrie.NewFieldTrie(stateTypes.FieldIndex(5), stateTypes.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
root, err := stateutil.RootsArrayHashTreeRoot(newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
@@ -30,7 +30,7 @@ func TestFieldTrie_NewTrie(t *testing.T) {
func TestFieldTrie_RecomputeTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
// 10 represents the enum value of validators
trie, err := fieldtrie.NewFieldTrie(11, stateTypes.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit)
trie, err := fieldtrie.NewFieldTrie(stateTypes.FieldIndex(11), stateTypes.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit)
require.NoError(t, err)
changedIdx := []uint64{2, 29}
@@ -58,7 +58,7 @@ func TestFieldTrie_RecomputeTrie(t *testing.T) {
func TestFieldTrie_CopyTrieImmutable(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
// 12 represents the enum value of randao mixes.
trie, err := fieldtrie.NewFieldTrie(13, stateTypes.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector))
trie, err := fieldtrie.NewFieldTrie(stateTypes.FieldIndex(13), stateTypes.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector))
require.NoError(t, err)
newTrie := trie.CopyTrie()

View File

@@ -0,0 +1,76 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"getters_attestation.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_participation.go",
"getters_payload_header.go",
"getters_randao.go",
"getters_state.go",
"getters_sync_committee.go",
"getters_validator.go",
"proofs.go",
"readonly_validator.go",
"setters_attestation.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_participation.go",
"setters_payload_header.go",
"setters_randao.go",
"setters_state.go",
"setters_sync_committee.go",
"setters_validator.go",
"ssz.go",
"state_trie.go",
"types.go",
] + select({
"//config:mainnet": ["beacon_state_mainnet.go"],
"//config:minimal": ["beacon_state_minimal.go"],
}),
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native",
visibility = [
"//beacon-chain:__subpackages__",
"//contracts/deposit:__subpackages__",
"//proto/migration:__subpackages__",
"//proto/prysm/v1alpha1:__subpackages__",
"//proto/testing:__subpackages__",
"//runtime/interop:__subpackages__",
"//slasher/rpc:__subpackages__",
"//testing/benchmark:__pkg__",
"//testing/fuzz:__pkg__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
"//tools/benchmark-files-gen:__pkg__",
"//tools/pcli:__pkg__",
],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/fieldtrie:go_default_library",
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//beacon-chain/state/state-native/types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@@ -1,7 +1,7 @@
//go:build !minimal
// +build !minimal
package v3
package state_native
import (
"sync"
@@ -10,14 +10,15 @@ import (
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// BeaconState defines a struct containing utilities for the eth2 chain state, defining
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
version int
genesisTime uint64 `ssz-gen:"true"`
genesisValidatorsRoot customtypes.Byte32 `ssz-gen:"true" ssz-size:"32"`
slot eth2types.Slot `ssz-gen:"true"`
@@ -33,6 +34,8 @@ type BeaconState struct {
balances []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
randaoMixes *customtypes.RandaoMixes `ssz-gen:"true" ssz-size:"65536,32"`
slashings []uint64 `ssz-gen:"true" ssz-size:"8192"`
previousEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"4096"`
currentEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"4096"`
previousEpochParticipation []byte `ssz-gen:"true" ssz-max:"1099511627776"`
currentEpochParticipation []byte `ssz-gen:"true" ssz-max:"1099511627776"`
justificationBits bitfield.Bitvector4 `ssz-gen:"true" ssz-size:"1"`
@@ -45,11 +48,12 @@ type BeaconState struct {
latestExecutionPayloadHeader *ethpb.ExecutionPayloadHeader `ssz-gen:"true"`
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
fieldIndexesRev map[nativetypes.FieldIndex]int
dirtyFields map[nativetypes.FieldIndex]bool
dirtyIndices map[nativetypes.FieldIndex][]uint64
stateFieldLeaves map[nativetypes.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[nativetypes.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
sharedFieldReferences map[nativetypes.FieldIndex]*stateutil.Reference
}

View File

@@ -1,7 +1,7 @@
//go:build minimal
// +build minimal
package v3
package state_native
import (
"sync"
@@ -10,14 +10,15 @@ import (
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// BeaconState defines a struct containing utilities for the eth2 chain state, defining
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
version int
genesisTime uint64 `ssz-gen:"true"`
genesisValidatorsRoot customtypes.Byte32 `ssz-gen:"true" ssz-size:"32"`
slot eth2types.Slot `ssz-gen:"true"`
@@ -33,6 +34,8 @@ type BeaconState struct {
balances []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
randaoMixes *customtypes.RandaoMixes `ssz-gen:"true" ssz-size:"64,32"`
slashings []uint64 `ssz-gen:"true" ssz-size:"64"`
previousEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"1024"`
currentEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"1024"`
previousEpochParticipation []byte `ssz-gen:"true" ssz-max:"1099511627776"`
currentEpochParticipation []byte `ssz-gen:"true" ssz-max:"1099511627776"`
justificationBits bitfield.Bitvector4 `ssz-gen:"true" ssz-size:"1"`
@@ -45,11 +48,12 @@ type BeaconState struct {
latestExecutionPayloadHeader *ethpb.ExecutionPayloadHeader `ssz-gen:"true"`
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
fieldIndexesRev map[nativetypes.FieldIndex]int
dirtyFields map[nativetypes.FieldIndex]bool
dirtyIndices map[nativetypes.FieldIndex][]uint64
stateFieldLeaves map[nativetypes.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[nativetypes.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
sharedFieldReferences map[nativetypes.FieldIndex]*stateutil.Reference
}

View File

@@ -37,4 +37,4 @@
// Although it is technically possible to remove the short-circuit conditions
// from the external function, that would require every read to obtain a lock
// even if the data was not present, leading to potential slowdowns.
package v1
package state_native

View File

@@ -1,4 +1,4 @@
package v1
package state_native
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"

View File

@@ -1,4 +1,4 @@
package v1
package state_native
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package v3
package state_native
import (
"bytes"

View File

@@ -1,4 +1,4 @@
package v2
package state_native
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"

View File

@@ -1,9 +1,8 @@
package v1
package state_native
import (
types "github.com/prysmaticlabs/eth2-types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// GenesisTime of the beacon state as a uint64.
@@ -25,8 +24,8 @@ func (b *BeaconState) GenesisValidatorsRoot() []byte {
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (_ *BeaconState) Version() int {
return version.Phase0
func (b *BeaconState) Version() int {
return b.version
}
// Slot of the current beacon chain state.

View File

@@ -1,4 +1,4 @@
package v2
package state_native
// CurrentEpochParticipation corresponding to participation bits on the beacon chain.
func (b *BeaconState) CurrentEpochParticipation() ([]byte, error) {

View File

@@ -1,4 +1,4 @@
package v3
package state_native
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"

View File

@@ -1,4 +1,4 @@
package v3
package state_native
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package v1
package state_native
import (
"fmt"
@@ -116,9 +116,9 @@ func (b *BeaconState) stateRootAtIndex(idx uint64) ([32]byte, error) {
return b.stateRoots[idx], nil
}
// ProtobufBeaconState transforms an input into beacon state in the form of protobuf.
// ProtobufBeaconStatePhase0 transforms an input into beacon state in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconState(s interface{}) (*ethpb.BeaconState, error) {
func ProtobufBeaconStatePhase0(s interface{}) (*ethpb.BeaconState, error) {
pbState, ok := s.(*ethpb.BeaconState)
if !ok {
return nil, errors.New("input is not type ethpb.BeaconState")

View File

@@ -1,4 +1,4 @@
package v3
package state_native
import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"

View File

@@ -1,4 +1,4 @@
package v3
package state_native
import (
"fmt"
@@ -6,7 +6,6 @@ import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -111,7 +110,7 @@ func (b *BeaconState) ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (state.
b.lock.RLock()
defer b.lock.RUnlock()
return v1.NewValidator(b.validators[idx])
return NewValidator(b.validators[idx])
}
// ValidatorIndexByPubkey returns a given validator by its 48-byte public key.
@@ -164,7 +163,7 @@ func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyV
b.lock.RUnlock()
for i, v := range validators {
v, err := v1.NewValidator(v)
v, err := NewValidator(v)
if err != nil {
return err
}

View File

@@ -1,4 +1,4 @@
package v1
package state_native
import (
"context"
@@ -6,6 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
@@ -48,7 +49,7 @@ func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error)
epochRoot := bytesutil.ToBytes32(epochBuf)
proof := make([][]byte, 0)
proof = append(proof, epochRoot[:])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, b.fieldIndexesRev[nativetypes.FinalizedCheckpoint])
proof = append(proof, branch...)
return proof, nil
}

View File

@@ -1,4 +1,4 @@
package v1
package state_native
import (
"github.com/pkg/errors"

View File

@@ -1,8 +1,9 @@
package v1
package state_native
import (
"fmt"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -20,21 +21,21 @@ func (b *BeaconState) RotateAttestations() error {
}
func (b *BeaconState) setPreviousEpochAttestations(val []*ethpb.PendingAttestation) {
b.sharedFieldReferences[previousEpochAttestations].MinusRef()
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.PreviousEpochAttestations].MinusRef()
b.sharedFieldReferences[nativetypes.PreviousEpochAttestations] = stateutil.NewRef(1)
b.previousEpochAttestations = val
b.markFieldAsDirty(previousEpochAttestations)
b.rebuildTrie[previousEpochAttestations] = true
b.markFieldAsDirty(nativetypes.PreviousEpochAttestations)
b.rebuildTrie[nativetypes.PreviousEpochAttestations] = true
}
func (b *BeaconState) setCurrentEpochAttestations(val []*ethpb.PendingAttestation) {
b.sharedFieldReferences[currentEpochAttestations].MinusRef()
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.CurrentEpochAttestations].MinusRef()
b.sharedFieldReferences[nativetypes.CurrentEpochAttestations] = stateutil.NewRef(1)
b.currentEpochAttestations = val
b.markFieldAsDirty(currentEpochAttestations)
b.rebuildTrie[currentEpochAttestations] = true
b.markFieldAsDirty(nativetypes.CurrentEpochAttestations)
b.rebuildTrie[nativetypes.CurrentEpochAttestations] = true
}
// AppendCurrentEpochAttestations for the beacon state. Appends the new value
@@ -49,17 +50,17 @@ func (b *BeaconState) AppendCurrentEpochAttestations(val *ethpb.PendingAttestati
return fmt.Errorf("current pending attestation exceeds max length %d", max)
}
if b.sharedFieldReferences[currentEpochAttestations].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.CurrentEpochAttestations].Refs() > 1 {
// Copy elements in underlying array by reference.
atts = make([]*ethpb.PendingAttestation, len(b.currentEpochAttestations))
copy(atts, b.currentEpochAttestations)
b.sharedFieldReferences[currentEpochAttestations].MinusRef()
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.CurrentEpochAttestations].MinusRef()
b.sharedFieldReferences[nativetypes.CurrentEpochAttestations] = stateutil.NewRef(1)
}
b.currentEpochAttestations = append(atts, val)
b.markFieldAsDirty(currentEpochAttestations)
b.addDirtyIndices(currentEpochAttestations, []uint64{uint64(len(b.currentEpochAttestations) - 1)})
b.markFieldAsDirty(nativetypes.CurrentEpochAttestations)
b.addDirtyIndices(nativetypes.CurrentEpochAttestations, []uint64{uint64(len(b.currentEpochAttestations) - 1)})
return nil
}
@@ -75,15 +76,15 @@ func (b *BeaconState) AppendPreviousEpochAttestations(val *ethpb.PendingAttestat
return fmt.Errorf("previous pending attestation exceeds max length %d", max)
}
if b.sharedFieldReferences[previousEpochAttestations].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.PreviousEpochAttestations].Refs() > 1 {
atts = make([]*ethpb.PendingAttestation, len(b.previousEpochAttestations))
copy(atts, b.previousEpochAttestations)
b.sharedFieldReferences[previousEpochAttestations].MinusRef()
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.PreviousEpochAttestations].MinusRef()
b.sharedFieldReferences[nativetypes.PreviousEpochAttestations] = stateutil.NewRef(1)
}
b.previousEpochAttestations = append(atts, val)
b.markFieldAsDirty(previousEpochAttestations)
b.addDirtyIndices(previousEpochAttestations, []uint64{uint64(len(b.previousEpochAttestations) - 1)})
b.markFieldAsDirty(nativetypes.PreviousEpochAttestations)
b.addDirtyIndices(nativetypes.PreviousEpochAttestations, []uint64{uint64(len(b.previousEpochAttestations) - 1)})
return nil
}

View File

@@ -1,9 +1,10 @@
package v1
package state_native
import (
"fmt"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -15,7 +16,7 @@ func (b *BeaconState) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
defer b.lock.Unlock()
b.latestBlockHeader = ethpb.CopyBeaconBlockHeader(val)
b.markFieldAsDirty(latestBlockHeader)
b.markFieldAsDirty(nativetypes.LatestBlockHeader)
return nil
}
@@ -25,8 +26,8 @@ func (b *BeaconState) SetBlockRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[blockRoots].MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.BlockRoots].MinusRef()
b.sharedFieldReferences[nativetypes.BlockRoots] = stateutil.NewRef(1)
var rootsArr [fieldparams.BlockRootsLength][32]byte
for i := 0; i < len(rootsArr); i++ {
@@ -34,8 +35,8 @@ func (b *BeaconState) SetBlockRoots(val [][]byte) error {
}
roots := customtypes.BlockRoots(rootsArr)
b.blockRoots = &roots
b.markFieldAsDirty(blockRoots)
b.rebuildTrie[blockRoots] = true
b.markFieldAsDirty(nativetypes.BlockRoots)
b.rebuildTrie[nativetypes.BlockRoots] = true
return nil
}
@@ -49,19 +50,19 @@ func (b *BeaconState) UpdateBlockRootAtIndex(idx uint64, blockRoot [32]byte) err
defer b.lock.Unlock()
r := b.blockRoots
if ref := b.sharedFieldReferences[blockRoots]; ref.Refs() > 1 {
if ref := b.sharedFieldReferences[nativetypes.BlockRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
roots := *b.blockRoots
rootsCopy := roots
r = &rootsCopy
ref.MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.BlockRoots] = stateutil.NewRef(1)
}
r[idx] = blockRoot
b.blockRoots = r
b.markFieldAsDirty(blockRoots)
b.addDirtyIndices(blockRoots, []uint64{idx})
b.markFieldAsDirty(nativetypes.BlockRoots)
b.addDirtyIndices(nativetypes.BlockRoots, []uint64{idx})
return nil
}

View File

@@ -1,7 +1,8 @@
package v2
package state_native
import (
"github.com/prysmaticlabs/go-bitfield"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -11,7 +12,7 @@ func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
defer b.lock.Unlock()
b.justificationBits = val
b.markFieldAsDirty(justificationBits)
b.markFieldAsDirty(nativetypes.JustificationBits)
return nil
}
@@ -21,7 +22,7 @@ func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) erro
defer b.lock.Unlock()
b.previousJustifiedCheckpoint = val
b.markFieldAsDirty(previousJustifiedCheckpoint)
b.markFieldAsDirty(nativetypes.PreviousJustifiedCheckpoint)
return nil
}
@@ -31,7 +32,7 @@ func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error
defer b.lock.Unlock()
b.currentJustifiedCheckpoint = val
b.markFieldAsDirty(currentJustifiedCheckpoint)
b.markFieldAsDirty(nativetypes.CurrentJustifiedCheckpoint)
return nil
}
@@ -41,6 +42,6 @@ func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
defer b.lock.Unlock()
b.finalizedCheckpoint = val
b.markFieldAsDirty(finalizedCheckpoint)
b.markFieldAsDirty(nativetypes.FinalizedCheckpoint)
return nil
}

View File

@@ -1,6 +1,7 @@
package v3
package state_native
import (
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -11,7 +12,7 @@ func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
defer b.lock.Unlock()
b.eth1Data = val
b.markFieldAsDirty(eth1Data)
b.markFieldAsDirty(nativetypes.Eth1Data)
return nil
}
@@ -21,12 +22,12 @@ func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Eth1DataVotes].MinusRef()
b.sharedFieldReferences[nativetypes.Eth1DataVotes] = stateutil.NewRef(1)
b.eth1DataVotes = val
b.markFieldAsDirty(eth1DataVotes)
b.rebuildTrie[eth1DataVotes] = true
b.markFieldAsDirty(nativetypes.Eth1DataVotes)
b.rebuildTrie[nativetypes.Eth1DataVotes] = true
return nil
}
@@ -36,7 +37,7 @@ func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
defer b.lock.Unlock()
b.eth1DepositIndex = val
b.markFieldAsDirty(eth1DepositIndex)
b.markFieldAsDirty(nativetypes.Eth1DepositIndex)
return nil
}
@@ -47,16 +48,16 @@ func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
defer b.lock.Unlock()
votes := b.eth1DataVotes
if b.sharedFieldReferences[eth1DataVotes].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.Eth1DataVotes].Refs() > 1 {
// Copy elements in underlying array by reference.
votes = make([]*ethpb.Eth1Data, len(b.eth1DataVotes))
copy(votes, b.eth1DataVotes)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Eth1DataVotes].MinusRef()
b.sharedFieldReferences[nativetypes.Eth1DataVotes] = stateutil.NewRef(1)
}
b.eth1DataVotes = append(votes, val)
b.markFieldAsDirty(eth1DataVotes)
b.addDirtyIndices(eth1DataVotes, []uint64{uint64(len(b.eth1DataVotes) - 1)})
b.markFieldAsDirty(nativetypes.Eth1DataVotes)
b.addDirtyIndices(nativetypes.Eth1DataVotes, []uint64{uint64(len(b.eth1DataVotes) - 1)})
return nil
}

View File

@@ -1,10 +1,10 @@
package v1
package state_native
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
stateTypes "github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/crypto/hash"
@@ -47,7 +47,7 @@ func (b *BeaconState) SetGenesisTime(val uint64) error {
defer b.lock.Unlock()
b.genesisTime = val
b.markFieldAsDirty(genesisTime)
b.markFieldAsDirty(nativetypes.GenesisTime)
return nil
}
@@ -60,7 +60,7 @@ func (b *BeaconState) SetGenesisValidatorsRoot(val []byte) error {
return errors.New("incorrect validators root length")
}
b.genesisValidatorsRoot = bytesutil.ToBytes32(val)
b.markFieldAsDirty(genesisValidatorsRoot)
b.markFieldAsDirty(nativetypes.GenesisValidatorsRoot)
return nil
}
@@ -70,7 +70,7 @@ func (b *BeaconState) SetSlot(val types.Slot) error {
defer b.lock.Unlock()
b.slot = val
b.markFieldAsDirty(slot)
b.markFieldAsDirty(nativetypes.Slot)
return nil
}
@@ -84,7 +84,7 @@ func (b *BeaconState) SetFork(val *ethpb.Fork) error {
return errors.New("proto.Clone did not return a fork proto")
}
b.fork = fk
b.markFieldAsDirty(fork)
b.markFieldAsDirty(nativetypes.Fork)
return nil
}
@@ -94,15 +94,15 @@ func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.HistoricalRoots].MinusRef()
b.sharedFieldReferences[nativetypes.HistoricalRoots] = stateutil.NewRef(1)
roots := make([][32]byte, len(val))
for i, r := range val {
copy(roots[i][:], r)
}
b.historicalRoots = roots
b.markFieldAsDirty(historicalRoots)
b.markFieldAsDirty(nativetypes.HistoricalRoots)
return nil
}
@@ -113,15 +113,15 @@ func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error {
defer b.lock.Unlock()
roots := b.historicalRoots
if b.sharedFieldReferences[historicalRoots].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.HistoricalRoots].Refs() > 1 {
roots = make([][32]byte, len(b.historicalRoots))
copy(roots, b.historicalRoots)
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.HistoricalRoots].MinusRef()
b.sharedFieldReferences[nativetypes.HistoricalRoots] = stateutil.NewRef(1)
}
b.historicalRoots = append(roots, root)
b.markFieldAsDirty(historicalRoots)
b.markFieldAsDirty(nativetypes.HistoricalRoots)
return nil
}
@@ -160,18 +160,18 @@ func (b *BeaconState) recomputeRoot(idx int) {
b.merkleLayers = layers
}
func (b *BeaconState) markFieldAsDirty(field stateTypes.FieldIndex) {
func (b *BeaconState) markFieldAsDirty(field nativetypes.FieldIndex) {
b.dirtyFields[field] = true
}
// addDirtyIndices adds the relevant dirty field indices, so that they
// can be recomputed.
func (b *BeaconState) addDirtyIndices(index stateTypes.FieldIndex, indices []uint64) {
func (b *BeaconState) addDirtyIndices(index nativetypes.FieldIndex, indices []uint64) {
if b.rebuildTrie[index] {
return
}
// Exit early if balance trie computation isn't enabled.
if !features.Get().EnableBalanceTrieComputation && index == balances {
if !features.Get().EnableBalanceTrieComputation && index == nativetypes.Balances {
return
}
totalIndicesLen := len(b.dirtyIndices[index]) + len(indices)

View File

@@ -1,6 +1,7 @@
package v2
package state_native
import (
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
)
@@ -10,12 +11,12 @@ func (b *BeaconState) SetPreviousParticipationBits(val []byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits] = stateutil.NewRef(1)
b.previousEpochParticipation = val
b.markFieldAsDirty(previousEpochParticipationBits)
b.rebuildTrie[previousEpochParticipationBits] = true
b.markFieldAsDirty(nativetypes.PreviousEpochParticipationBits)
b.rebuildTrie[nativetypes.PreviousEpochParticipationBits] = true
return nil
}
@@ -25,12 +26,12 @@ func (b *BeaconState) SetCurrentParticipationBits(val []byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits] = stateutil.NewRef(1)
b.currentEpochParticipation = val
b.markFieldAsDirty(currentEpochParticipationBits)
b.rebuildTrie[currentEpochParticipationBits] = true
b.markFieldAsDirty(nativetypes.CurrentEpochParticipationBits)
b.rebuildTrie[nativetypes.CurrentEpochParticipationBits] = true
return nil
}
@@ -41,17 +42,17 @@ func (b *BeaconState) AppendCurrentParticipationBits(val byte) error {
defer b.lock.Unlock()
participation := b.currentEpochParticipation
if b.sharedFieldReferences[currentEpochParticipationBits].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits].Refs() > 1 {
// Copy elements in underlying array by reference.
participation = make([]byte, len(b.currentEpochParticipation))
copy(participation, b.currentEpochParticipation)
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits] = stateutil.NewRef(1)
}
b.currentEpochParticipation = append(participation, val)
b.markFieldAsDirty(currentEpochParticipationBits)
b.addDirtyIndices(currentEpochParticipationBits, []uint64{uint64(len(b.currentEpochParticipation) - 1)})
b.markFieldAsDirty(nativetypes.CurrentEpochParticipationBits)
b.addDirtyIndices(nativetypes.CurrentEpochParticipationBits, []uint64{uint64(len(b.currentEpochParticipation) - 1)})
return nil
}
@@ -62,16 +63,16 @@ func (b *BeaconState) AppendPreviousParticipationBits(val byte) error {
defer b.lock.Unlock()
bits := b.previousEpochParticipation
if b.sharedFieldReferences[previousEpochParticipationBits].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits].Refs() > 1 {
bits = make([]byte, len(b.previousEpochParticipation))
copy(bits, b.previousEpochParticipation)
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits] = stateutil.NewRef(1)
}
b.previousEpochParticipation = append(bits, val)
b.markFieldAsDirty(previousEpochParticipationBits)
b.addDirtyIndices(previousEpochParticipationBits, []uint64{uint64(len(b.previousEpochParticipation) - 1)})
b.markFieldAsDirty(nativetypes.PreviousEpochParticipationBits)
b.addDirtyIndices(nativetypes.PreviousEpochParticipationBits, []uint64{uint64(len(b.previousEpochParticipation) - 1)})
return nil
}
@@ -82,12 +83,12 @@ func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) (
b.lock.Lock()
participation := b.previousEpochParticipation
if b.sharedFieldReferences[previousEpochParticipationBits].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits].Refs() > 1 {
// Copy elements in underlying array by reference.
participation = make([]byte, len(b.previousEpochParticipation))
copy(participation, b.previousEpochParticipation)
b.sharedFieldReferences[previousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[previousEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.PreviousEpochParticipationBits] = stateutil.NewRef(1)
}
// Lock is released so that mutator can
// acquire it.
@@ -101,8 +102,8 @@ func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) (
b.lock.Lock()
defer b.lock.Unlock()
b.previousEpochParticipation = participation
b.markFieldAsDirty(previousEpochParticipationBits)
b.rebuildTrie[previousEpochParticipationBits] = true
b.markFieldAsDirty(nativetypes.PreviousEpochParticipationBits)
b.rebuildTrie[nativetypes.PreviousEpochParticipationBits] = true
return nil
}
@@ -112,12 +113,12 @@ func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([
b.lock.Lock()
participation := b.currentEpochParticipation
if b.sharedFieldReferences[currentEpochParticipationBits].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits].Refs() > 1 {
// Copy elements in underlying array by reference.
participation = make([]byte, len(b.currentEpochParticipation))
copy(participation, b.currentEpochParticipation)
b.sharedFieldReferences[currentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[currentEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits].MinusRef()
b.sharedFieldReferences[nativetypes.CurrentEpochParticipationBits] = stateutil.NewRef(1)
}
// Lock is released so that mutator can
// acquire it.
@@ -131,7 +132,7 @@ func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([
b.lock.Lock()
defer b.lock.Unlock()
b.currentEpochParticipation = participation
b.markFieldAsDirty(currentEpochParticipationBits)
b.rebuildTrie[currentEpochParticipationBits] = true
b.markFieldAsDirty(nativetypes.CurrentEpochParticipationBits)
b.rebuildTrie[nativetypes.CurrentEpochParticipationBits] = true
return nil
}

View File

@@ -1,6 +1,9 @@
package v3
package state_native
import ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
import (
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetLatestExecutionPayloadHeader for the beacon state.
func (b *BeaconState) SetLatestExecutionPayloadHeader(val *ethpb.ExecutionPayloadHeader) error {
@@ -8,6 +11,6 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val *ethpb.ExecutionPayloa
defer b.lock.Unlock()
b.latestExecutionPayloadHeader = val
b.markFieldAsDirty(latestExecutionPayloadHeader)
b.markFieldAsDirty(nativetypes.LatestExecutionPayloadHeader)
return nil
}

View File

@@ -1,8 +1,9 @@
package v1
package state_native
import (
"github.com/pkg/errors"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -14,8 +15,8 @@ func (b *BeaconState) SetRandaoMixes(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.RandaoMixes].MinusRef()
b.sharedFieldReferences[nativetypes.RandaoMixes] = stateutil.NewRef(1)
var mixesArr [fieldparams.RandaoMixesLength][32]byte
for i := 0; i < len(mixesArr); i++ {
@@ -23,8 +24,8 @@ func (b *BeaconState) SetRandaoMixes(val [][]byte) error {
}
mixes := customtypes.RandaoMixes(mixesArr)
b.randaoMixes = &mixes
b.markFieldAsDirty(randaoMixes)
b.rebuildTrie[randaoMixes] = true
b.markFieldAsDirty(nativetypes.RandaoMixes)
b.rebuildTrie[nativetypes.RandaoMixes] = true
return nil
}
@@ -38,19 +39,19 @@ func (b *BeaconState) UpdateRandaoMixesAtIndex(idx uint64, val []byte) error {
defer b.lock.Unlock()
mixes := b.randaoMixes
if refs := b.sharedFieldReferences[randaoMixes].Refs(); refs > 1 {
if refs := b.sharedFieldReferences[nativetypes.RandaoMixes].Refs(); refs > 1 {
// Copy elements in underlying array by reference.
m := *b.randaoMixes
mCopy := m
mixes = &mCopy
b.sharedFieldReferences[randaoMixes].MinusRef()
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.RandaoMixes].MinusRef()
b.sharedFieldReferences[nativetypes.RandaoMixes] = stateutil.NewRef(1)
}
mixes[idx] = bytesutil.ToBytes32(val)
b.randaoMixes = mixes
b.markFieldAsDirty(randaoMixes)
b.addDirtyIndices(randaoMixes, []uint64{idx})
b.markFieldAsDirty(nativetypes.RandaoMixes)
b.addDirtyIndices(nativetypes.RandaoMixes, []uint64{idx})
return nil
}

View File

@@ -1,8 +1,9 @@
package v1
package state_native
import (
"github.com/pkg/errors"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
)
@@ -13,8 +14,8 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[stateRoots].MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.StateRoots].MinusRef()
b.sharedFieldReferences[nativetypes.StateRoots] = stateutil.NewRef(1)
var rootsArr [fieldparams.StateRootsLength][32]byte
for i := 0; i < len(rootsArr); i++ {
@@ -22,8 +23,8 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error {
}
roots := customtypes.StateRoots(rootsArr)
b.stateRoots = &roots
b.markFieldAsDirty(stateRoots)
b.rebuildTrie[stateRoots] = true
b.markFieldAsDirty(nativetypes.StateRoots)
b.rebuildTrie[nativetypes.StateRoots] = true
return nil
}
@@ -42,19 +43,19 @@ func (b *BeaconState) UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) err
// Check if we hold the only reference to the shared state roots slice.
r := b.stateRoots
if ref := b.sharedFieldReferences[stateRoots]; ref.Refs() > 1 {
if ref := b.sharedFieldReferences[nativetypes.StateRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
roots := *b.stateRoots
rootsCopy := roots
r = &rootsCopy
ref.MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.StateRoots] = stateutil.NewRef(1)
}
r[idx] = stateRoot
b.stateRoots = r
b.markFieldAsDirty(stateRoots)
b.addDirtyIndices(stateRoots, []uint64{idx})
b.markFieldAsDirty(nativetypes.StateRoots)
b.addDirtyIndices(nativetypes.StateRoots, []uint64{idx})
return nil
}

View File

@@ -1,6 +1,7 @@
package v3
package state_native
import (
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -10,7 +11,7 @@ func (b *BeaconState) SetCurrentSyncCommittee(val *ethpb.SyncCommittee) error {
defer b.lock.Unlock()
b.currentSyncCommittee = val
b.markFieldAsDirty(currentSyncCommittee)
b.markFieldAsDirty(nativetypes.CurrentSyncCommittee)
return nil
}
@@ -20,6 +21,6 @@ func (b *BeaconState) SetNextSyncCommittee(val *ethpb.SyncCommittee) error {
defer b.lock.Unlock()
b.nextSyncCommittee = val
b.markFieldAsDirty(nextSyncCommittee)
b.markFieldAsDirty(nativetypes.NextSyncCommittee)
return nil
}

View File

@@ -1,8 +1,9 @@
package v3
package state_native
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -15,10 +16,10 @@ func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
defer b.lock.Unlock()
b.validators = val
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.markFieldAsDirty(validators)
b.rebuildTrie[validators] = true
b.sharedFieldReferences[nativetypes.Validators].MinusRef()
b.sharedFieldReferences[nativetypes.Validators] = stateutil.NewRef(1)
b.markFieldAsDirty(nativetypes.Validators)
b.rebuildTrie[nativetypes.Validators] = true
b.valMapHandler = stateutil.NewValMapHandler(b.validators)
return nil
}
@@ -28,10 +29,10 @@ func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error {
b.lock.Lock()
v := b.validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
if ref := b.sharedFieldReferences[nativetypes.Validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Validators] = stateutil.NewRef(1)
}
b.lock.Unlock()
var changedVals []uint64
@@ -50,8 +51,8 @@ func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator
defer b.lock.Unlock()
b.validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, changedVals)
b.markFieldAsDirty(nativetypes.Validators)
b.addDirtyIndices(nativetypes.Validators, changedVals)
return nil
}
@@ -66,16 +67,16 @@ func (b *BeaconState) UpdateValidatorAtIndex(idx types.ValidatorIndex, val *ethp
defer b.lock.Unlock()
v := b.validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
if ref := b.sharedFieldReferences[nativetypes.Validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Validators] = stateutil.NewRef(1)
}
v[idx] = val
b.validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(idx)})
b.markFieldAsDirty(nativetypes.Validators)
b.addDirtyIndices(nativetypes.Validators, []uint64{uint64(idx)})
return nil
}
@@ -86,12 +87,12 @@ func (b *BeaconState) SetBalances(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Balances].MinusRef()
b.sharedFieldReferences[nativetypes.Balances] = stateutil.NewRef(1)
b.balances = val
b.markFieldAsDirty(balances)
b.rebuildTrie[balances] = true
b.markFieldAsDirty(nativetypes.Balances)
b.rebuildTrie[nativetypes.Balances] = true
return nil
}
@@ -105,16 +106,16 @@ func (b *BeaconState) UpdateBalancesAtIndex(idx types.ValidatorIndex, val uint64
defer b.lock.Unlock()
bals := b.balances
if b.sharedFieldReferences[balances].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.Balances].Refs() > 1 {
bals = b.balancesVal()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Balances].MinusRef()
b.sharedFieldReferences[nativetypes.Balances] = stateutil.NewRef(1)
}
bals[idx] = val
b.balances = bals
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(idx)})
b.markFieldAsDirty(nativetypes.Balances)
b.addDirtyIndices(nativetypes.Balances, []uint64{uint64(idx)})
return nil
}
@@ -124,11 +125,11 @@ func (b *BeaconState) SetSlashings(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Slashings].MinusRef()
b.sharedFieldReferences[nativetypes.Slashings] = stateutil.NewRef(1)
b.slashings = val
b.markFieldAsDirty(slashings)
b.markFieldAsDirty(nativetypes.Slashings)
return nil
}
@@ -142,17 +143,17 @@ func (b *BeaconState) UpdateSlashingsAtIndex(idx, val uint64) error {
defer b.lock.Unlock()
s := b.slashings
if b.sharedFieldReferences[slashings].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.Slashings].Refs() > 1 {
s = b.slashingsVal()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Slashings].MinusRef()
b.sharedFieldReferences[nativetypes.Slashings] = stateutil.NewRef(1)
}
s[idx] = val
b.slashings = s
b.markFieldAsDirty(slashings)
b.markFieldAsDirty(nativetypes.Slashings)
return nil
}
@@ -163,10 +164,10 @@ func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
defer b.lock.Unlock()
vals := b.validators
if b.sharedFieldReferences[validators].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.Validators].Refs() > 1 {
vals = b.validatorsReferences()
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Validators].MinusRef()
b.sharedFieldReferences[nativetypes.Validators] = stateutil.NewRef(1)
}
// append validator to slice
@@ -175,8 +176,8 @@ func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
b.valMapHandler.Set(bytesutil.ToBytes48(val.PublicKey), valIdx)
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(valIdx)})
b.markFieldAsDirty(nativetypes.Validators)
b.addDirtyIndices(nativetypes.Validators, []uint64{uint64(valIdx)})
return nil
}
@@ -187,16 +188,16 @@ func (b *BeaconState) AppendBalance(bal uint64) error {
defer b.lock.Unlock()
bals := b.balances
if b.sharedFieldReferences[balances].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.Balances].Refs() > 1 {
bals = b.balancesVal()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.Balances].MinusRef()
b.sharedFieldReferences[nativetypes.Balances] = stateutil.NewRef(1)
}
b.balances = append(bals, bal)
balIdx := len(b.balances) - 1
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(balIdx)})
b.markFieldAsDirty(nativetypes.Balances)
b.addDirtyIndices(nativetypes.Balances, []uint64{uint64(balIdx)})
return nil
}
@@ -206,14 +207,14 @@ func (b *BeaconState) AppendInactivityScore(s uint64) error {
defer b.lock.Unlock()
scores := b.inactivityScores
if b.sharedFieldReferences[inactivityScores].Refs() > 1 {
if b.sharedFieldReferences[nativetypes.InactivityScores].Refs() > 1 {
scores = b.inactivityScoresVal()
b.sharedFieldReferences[inactivityScores].MinusRef()
b.sharedFieldReferences[inactivityScores] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.InactivityScores].MinusRef()
b.sharedFieldReferences[nativetypes.InactivityScores] = stateutil.NewRef(1)
}
b.inactivityScores = append(scores, s)
b.markFieldAsDirty(inactivityScores)
b.markFieldAsDirty(nativetypes.InactivityScores)
return nil
}
@@ -223,10 +224,10 @@ func (b *BeaconState) SetInactivityScores(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[inactivityScores].MinusRef()
b.sharedFieldReferences[inactivityScores] = stateutil.NewRef(1)
b.sharedFieldReferences[nativetypes.InactivityScores].MinusRef()
b.sharedFieldReferences[nativetypes.InactivityScores] = stateutil.NewRef(1)
b.inactivityScores = val
b.markFieldAsDirty(inactivityScores)
b.markFieldAsDirty(nativetypes.InactivityScores)
return nil
}

View File

@@ -0,0 +1,36 @@
package state_native
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
var errAssertionFailed = errors.New("failed to convert interface to proto state")
var errUnsupportedVersion = errors.New("unsupported beacon state version")
func (b *BeaconState) MarshalSSZ() ([]byte, error) {
proto := b.ToProto()
switch b.Version() {
case version.Phase0:
s, ok := proto.(*ethpb.BeaconState)
if !ok {
return nil, errAssertionFailed
}
return s.MarshalSSZ()
case version.Altair:
s, ok := proto.(*ethpb.BeaconStateAltair)
if !ok {
return nil, errAssertionFailed
}
return s.MarshalSSZ()
case version.Bellatrix:
s, ok := proto.(*ethpb.BeaconStateBellatrix)
if !ok {
return nil, errAssertionFailed
}
return s.MarshalSSZ()
default:
return nil, errUnsupportedVersion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
)
// Ensure type BeaconState below implements BeaconState interface.
var _ state.BeaconState = (*BeaconState)(nil)
func init() {
fieldMap = make(map[nativetypes.FieldIndex]types.DataType)
// Initialize the fixed sized arrays.
fieldMap[nativetypes.BlockRoots] = types.BasicArray
fieldMap[nativetypes.StateRoots] = types.BasicArray
fieldMap[nativetypes.RandaoMixes] = types.BasicArray
// Initialize the composite arrays.
fieldMap[nativetypes.Eth1DataVotes] = types.CompositeArray
fieldMap[nativetypes.Validators] = types.CompositeArray
fieldMap[nativetypes.PreviousEpochAttestations] = types.CompositeArray
fieldMap[nativetypes.CurrentEpochAttestations] = types.CompositeArray
fieldMap[nativetypes.Balances] = types.CompressedArray
}
// fieldMap keeps track of each field
// to its corresponding data type.
var fieldMap map[nativetypes.FieldIndex]types.DataType

View File

@@ -0,0 +1,9 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["types.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types",
visibility = ["//visibility:public"],
deps = ["@com_github_pkg_errors//:go_default_library"],
)

View File

@@ -0,0 +1,124 @@
package types
import (
"github.com/pkg/errors"
)
// FieldIndex represents the relevant field position in the
// state struct for a field.
type FieldIndex int
// String returns the name of the field index.
func (f FieldIndex) String(stateVersion int) string {
switch f {
case GenesisTime:
return "genesisTime"
case GenesisValidatorsRoot:
return "genesisValidatorsRoot"
case Slot:
return "slot"
case Fork:
return "fork"
case LatestBlockHeader:
return "latestBlockHeader"
case BlockRoots:
return "blockRoots"
case StateRoots:
return "stateRoots"
case HistoricalRoots:
return "historicalRoots"
case Eth1Data:
return "eth1Data"
case Eth1DataVotes:
return "eth1DataVotes"
case Eth1DepositIndex:
return "eth1DepositIndex"
case Validators:
return "validators"
case Balances:
return "balances"
case RandaoMixes:
return "randaoMixes"
case Slashings:
return "slashings"
case PreviousEpochAttestations:
return "previousEpochAttestations"
case CurrentEpochAttestations:
return "currentEpochAttestations"
case PreviousEpochParticipationBits:
return "previousEpochParticipationBits"
case CurrentEpochParticipationBits:
return "currentEpochParticipationBits"
case JustificationBits:
return "justificationBits"
case PreviousJustifiedCheckpoint:
return "previousJustifiedCheckpoint"
case CurrentJustifiedCheckpoint:
return "currentJustifiedCheckpoint"
case FinalizedCheckpoint:
return "finalizedCheckpoint"
case InactivityScores:
return "inactivityScores"
case CurrentSyncCommittee:
return "currentSyncCommittee"
case NextSyncCommittee:
return "nextSyncCommittee"
case LatestExecutionPayloadHeader:
return "latestExecutionPayloadHeader"
default:
return ""
}
}
// ElemsInChunk returns the number of elements in the chunk (number of
// elements that are able to be packed).
func (f FieldIndex) ElemsInChunk() (uint64, error) {
switch f {
case Balances:
return 4, nil
default:
return 0, errors.Errorf("field %d doesn't support element compression", f)
}
}
func (f FieldIndex) Native() bool {
return true
}
// Below we define a set of useful enum values for the field
// indices of the beacon state. For example, genesisTime is the
// 0th field of the beacon state. This is helpful when we are
// updating the Merkle branches up the trie representation
// of the beacon state. The below field indexes correspond
// to the v1 state.
const (
GenesisTime FieldIndex = iota
GenesisValidatorsRoot
Slot
Fork
LatestBlockHeader
BlockRoots
StateRoots
HistoricalRoots
Eth1Data
Eth1DataVotes
Eth1DepositIndex
Validators
Balances
RandaoMixes
Slashings
PreviousEpochAttestations
CurrentEpochAttestations
PreviousEpochParticipationBits
CurrentEpochParticipationBits
JustificationBits
PreviousJustifiedCheckpoint
CurrentJustifiedCheckpoint
FinalizedCheckpoint
// State Fields Added in Altair.
InactivityScores
CurrentSyncCommittee
NextSyncCommittee
// State fields added in Bellatrix.
LatestExecutionPayloadHeader
)

View File

@@ -1,134 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
load("//proto:ssz_proto_library.bzl", "ssz_proto_files")
load("//tools:ssz.bzl", "SSZ_DEPS", "ssz_gen_marshal")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"field_roots.go",
"getters_attestation.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_randao.go",
"getters_state.go",
"getters_validator.go",
"proofs.go",
"readonly_validator.go",
"setters_attestation.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_randao.go",
"setters_state.go",
"setters_validator.go",
"state_trie.go",
"types.go",
"unsupported_getters.go",
"unsupported_setters.go",
":ssz_generated_files", # keep
] + select({
"//config:mainnet": ["beacon_state_mainnet.go"],
"//config:minimal": ["beacon_state_minimal.go"],
}),
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1",
visibility = [
"//beacon-chain:__subpackages__",
"//contracts/deposit:__subpackages__",
"//proto/migration:__subpackages__",
"//proto/prysm/v1alpha1:__subpackages__",
"//proto/testing:__subpackages__",
"//runtime/interop:__subpackages__",
"//slasher/rpc:__subpackages__",
"//testing/benchmark:__pkg__",
"//testing/fuzz:__pkg__",
"//testing/spectest:__subpackages__",
"//testing/util:__pkg__",
"//tools/benchmark-files-gen:__pkg__",
"//tools/pcli:__pkg__",
],
deps = [
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/fieldtrie:go_default_library",
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/types:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_ferranbt_fastssz//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
# gazelle:exclude types_bench_test.go
go_test(
name = "go_default_test",
srcs = [
"getters_attestation_test.go",
"getters_block_test.go",
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"readonly_validator_test.go",
"references_test.go",
"setters_attestation_test.go",
"state_test.go",
"state_trie_test.go",
"types_test.go",
] + select({
"//config:mainnet": ["beacon_state_mainnet_test.go"],
"//config:minimal": ["beacon_state_minimal_test.go"],
}),
embed = [":go_default_library"],
deps = [
"//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",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/interop:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
ssz_gen_marshal(
name = "ssz_generated_files",
srcs = select({
"//config:mainnet": ["beacon_state_mainnet.go"],
"//config:minimal": ["beacon_state_minimal.go"],
}),
includes = [
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//config/fieldparams:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
],
objs = [
"BeaconState[no-htr]",
],
)

View File

@@ -1,51 +0,0 @@
//go:build !minimal
// +build !minimal
package v1
import (
"sync"
eth2types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
genesisTime uint64 `ssz-gen:"true"`
genesisValidatorsRoot customtypes.Byte32 `ssz-gen:"true" ssz-size:"32"`
slot eth2types.Slot `ssz-gen:"true"`
fork *ethpb.Fork `ssz-gen:"true"`
latestBlockHeader *ethpb.BeaconBlockHeader `ssz-gen:"true"`
blockRoots *customtypes.BlockRoots `ssz-gen:"true" ssz-size:"8192,32"`
stateRoots *customtypes.StateRoots `ssz-gen:"true" ssz-size:"8192,32"`
historicalRoots customtypes.HistoricalRoots `ssz-gen:"true" ssz-size:"?,32" ssz-max:"16777216"`
eth1Data *ethpb.Eth1Data `ssz-gen:"true"`
eth1DataVotes []*ethpb.Eth1Data `ssz-gen:"true" ssz-max:"2048"`
eth1DepositIndex uint64 `ssz-gen:"true"`
validators []*ethpb.Validator `ssz-gen:"true" ssz-max:"1099511627776"`
balances []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
randaoMixes *customtypes.RandaoMixes `ssz-gen:"true" ssz-size:"65536,32"`
slashings []uint64 `ssz-gen:"true" ssz-size:"8192"`
previousEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"4096"`
currentEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"4096"`
justificationBits bitfield.Bitvector4 `ssz-gen:"true" ssz-size:"1"`
previousJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
currentJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
finalizedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
}

View File

@@ -1,82 +0,0 @@
//go:build !minimal
// +build !minimal
package v1
import (
"reflect"
"strconv"
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestMainnetSszValuesAgainstFieldParams(t *testing.T) {
// Casting needed to avoid lock copy analyzer issue.
bs := (interface{})(BeaconState{})
bsType := reflect.TypeOf(bs)
f, ok := bsType.FieldByName("genesisValidatorsRoot")
require.Equal(t, true, ok, "Required field not found")
v := f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("blockRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.BlockRootsLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("stateRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.StateRootsLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("historicalRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "?,"+strconv.Itoa(fieldparams.RootLength), v)
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.HistoricalRootsLength), v)
f, ok = bsType.FieldByName("eth1DataVotes")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.Eth1DataVotesLength), v)
f, ok = bsType.FieldByName("validators")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
f, ok = bsType.FieldByName("balances")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
f, ok = bsType.FieldByName("randaoMixes")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.RandaoMixesLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("slashings")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.SlashingsLength), v)
f, ok = bsType.FieldByName("previousEpochAttestations")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.PreviousEpochAttestationsLength), v)
f, ok = bsType.FieldByName("currentEpochAttestations")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.CurrentEpochAttestationsLength), v)
f, ok = bsType.FieldByName("justificationBits")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "1", v)
}

View File

@@ -1,51 +0,0 @@
//go:build minimal
// +build minimal
package v1
import (
"sync"
eth2types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// BeaconState defines a struct containing utilities for the Ethereum Beacon Chain state, defining
// getters and setters for its respective values and helpful functions such as HashTreeRoot().
type BeaconState struct {
genesisTime uint64 `ssz-gen:"true"`
genesisValidatorsRoot customtypes.Byte32 `ssz-gen:"true" ssz-size:"32"`
slot eth2types.Slot `ssz-gen:"true"`
fork *ethpb.Fork `ssz-gen:"true"`
latestBlockHeader *ethpb.BeaconBlockHeader `ssz-gen:"true"`
blockRoots *customtypes.BlockRoots `ssz-gen:"true" ssz-size:"64,32"`
stateRoots *customtypes.StateRoots `ssz-gen:"true" ssz-size:"64,32"`
historicalRoots customtypes.HistoricalRoots `ssz-gen:"true" ssz-size:"?,32" ssz-max:"16777216"`
eth1Data *ethpb.Eth1Data `ssz-gen:"true"`
eth1DataVotes []*ethpb.Eth1Data `ssz-gen:"true" ssz-max:"32"`
eth1DepositIndex uint64 `ssz-gen:"true"`
validators []*ethpb.Validator `ssz-gen:"true" ssz-max:"1099511627776"`
balances []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
randaoMixes *customtypes.RandaoMixes `ssz-gen:"true" ssz-size:"64,32"`
slashings []uint64 `ssz-gen:"true" ssz-size:"64"`
previousEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"1024"`
currentEpochAttestations []*ethpb.PendingAttestation `ssz-gen:"true" ssz-max:"1024"`
justificationBits bitfield.Bitvector4 `ssz-gen:"true" ssz-size:"1"`
previousJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
currentJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
finalizedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
dirtyIndices map[types.FieldIndex][]uint64
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
}

View File

@@ -1,82 +0,0 @@
//go:build minimal
// +build minimal
package v1
import (
"reflect"
"strconv"
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestMinimalSszValuesAgainstFieldParams(t *testing.T) {
// Casting needed to avoid lock copy analyzer issue.
bs := (interface{})(BeaconState{})
bsType := reflect.TypeOf(bs)
f, ok := bsType.FieldByName("genesisValidatorsRoot")
require.Equal(t, true, ok, "Required field not found")
v := f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("blockRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.BlockRootsLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("stateRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.StateRootsLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("historicalRoots")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "?,"+strconv.Itoa(fieldparams.RootLength), v)
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.HistoricalRootsLength), v)
f, ok = bsType.FieldByName("eth1DataVotes")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.Eth1DataVotesLength), v)
f, ok = bsType.FieldByName("validators")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
f, ok = bsType.FieldByName("balances")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
f, ok = bsType.FieldByName("randaoMixes")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.RandaoMixesLength)+","+strconv.Itoa(fieldparams.RootLength), v)
f, ok = bsType.FieldByName("slashings")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, strconv.Itoa(fieldparams.SlashingsLength), v)
f, ok = bsType.FieldByName("previousEpochAttestations")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.PreviousEpochAttestationsLength), v)
f, ok = bsType.FieldByName("currentEpochAttestations")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.CurrentEpochAttestationsLength), v)
f, ok = bsType.FieldByName("justificationBits")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "1", v)
}

View File

@@ -1,14 +0,0 @@
package v1
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
return stateutil.ComputeFieldRootsWithHasherPhase0(ctx, state)
}

View File

@@ -1,493 +0,0 @@
// Code generated by fastssz. DO NOT EDIT.
// Hash: 2e923b42b8e4fcc278301da6506b212334a78169cb32c70e0d66a636435b8925
package v1
import (
ssz "github.com/ferranbt/fastssz"
eth2types "github.com/prysmaticlabs/eth2-types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// MarshalSSZ ssz marshals the BeaconState object
func (b *BeaconState) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(b)
}
// MarshalSSZTo ssz marshals the BeaconState object to a target array
func (b *BeaconState) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
offset := int(2687377)
// Field (0) 'genesisTime'
dst = ssz.MarshalUint64(dst, b.genesisTime)
// Field (1) 'genesisValidatorsRoot'
dst = append(dst, b.genesisValidatorsRoot[:]...)
// Field (2) 'slot'
dst = ssz.MarshalUint64(dst, uint64(b.slot))
// Field (3) 'fork'
if b.fork == nil {
b.fork = new(ethpb.Fork)
}
if dst, err = b.fork.MarshalSSZTo(dst); err != nil {
return
}
// Field (4) 'latestBlockHeader'
if b.latestBlockHeader == nil {
b.latestBlockHeader = new(ethpb.BeaconBlockHeader)
}
if dst, err = b.latestBlockHeader.MarshalSSZTo(dst); err != nil {
return
}
// Field (5) 'blockRoots'
for ii := 0; ii < 8192; ii++ {
dst = append(dst, b.blockRoots[ii][:]...)
}
// Field (6) 'stateRoots'
for ii := 0; ii < 8192; ii++ {
dst = append(dst, b.stateRoots[ii][:]...)
}
// Offset (7) 'historicalRoots'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.historicalRoots) * 32
// Field (8) 'eth1Data'
if b.eth1Data == nil {
b.eth1Data = new(ethpb.Eth1Data)
}
if dst, err = b.eth1Data.MarshalSSZTo(dst); err != nil {
return
}
// Offset (9) 'eth1DataVotes'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.eth1DataVotes) * 72
// Field (10) 'eth1DepositIndex'
dst = ssz.MarshalUint64(dst, b.eth1DepositIndex)
// Offset (11) 'validators'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.validators) * 121
// Offset (12) 'balances'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.balances) * 8
// Field (13) 'randaoMixes'
for ii := 0; ii < 65536; ii++ {
dst = append(dst, b.randaoMixes[ii][:]...)
}
// Field (14) 'slashings'
if len(b.slashings) != 8192 {
err = ssz.ErrVectorLength
return
}
for ii := 0; ii < 8192; ii++ {
dst = ssz.MarshalUint64(dst, b.slashings[ii])
}
// Offset (15) 'previousEpochAttestations'
dst = ssz.WriteOffset(dst, offset)
for ii := 0; ii < len(b.previousEpochAttestations); ii++ {
offset += 4
offset += b.previousEpochAttestations[ii].SizeSSZ()
}
// Offset (16) 'currentEpochAttestations'
dst = ssz.WriteOffset(dst, offset)
for ii := 0; ii < len(b.currentEpochAttestations); ii++ {
offset += 4
offset += b.currentEpochAttestations[ii].SizeSSZ()
}
// Field (17) 'justificationBits'
if len(b.justificationBits) != 1 {
err = ssz.ErrBytesLength
return
}
dst = append(dst, b.justificationBits...)
// Field (18) 'previousJustifiedCheckpoint'
if b.previousJustifiedCheckpoint == nil {
b.previousJustifiedCheckpoint = new(ethpb.Checkpoint)
}
if dst, err = b.previousJustifiedCheckpoint.MarshalSSZTo(dst); err != nil {
return
}
// Field (19) 'currentJustifiedCheckpoint'
if b.currentJustifiedCheckpoint == nil {
b.currentJustifiedCheckpoint = new(ethpb.Checkpoint)
}
if dst, err = b.currentJustifiedCheckpoint.MarshalSSZTo(dst); err != nil {
return
}
// Field (20) 'finalizedCheckpoint'
if b.finalizedCheckpoint == nil {
b.finalizedCheckpoint = new(ethpb.Checkpoint)
}
if dst, err = b.finalizedCheckpoint.MarshalSSZTo(dst); err != nil {
return
}
// Field (7) 'historicalRoots'
if len(b.historicalRoots) > 16777216 {
err = ssz.ErrListTooBig
return
}
for ii := 0; ii < len(b.historicalRoots); ii++ {
dst = append(dst, b.historicalRoots[ii][:]...)
}
// Field (9) 'eth1DataVotes'
if len(b.eth1DataVotes) > 2048 {
err = ssz.ErrListTooBig
return
}
for ii := 0; ii < len(b.eth1DataVotes); ii++ {
if dst, err = b.eth1DataVotes[ii].MarshalSSZTo(dst); err != nil {
return
}
}
// Field (11) 'validators'
if len(b.validators) > 1099511627776 {
err = ssz.ErrListTooBig
return
}
for ii := 0; ii < len(b.validators); ii++ {
if dst, err = b.validators[ii].MarshalSSZTo(dst); err != nil {
return
}
}
// Field (12) 'balances'
if len(b.balances) > 1099511627776 {
err = ssz.ErrListTooBig
return
}
for ii := 0; ii < len(b.balances); ii++ {
dst = ssz.MarshalUint64(dst, b.balances[ii])
}
// Field (15) 'previousEpochAttestations'
if len(b.previousEpochAttestations) > 4096 {
err = ssz.ErrListTooBig
return
}
{
offset = 4 * len(b.previousEpochAttestations)
for ii := 0; ii < len(b.previousEpochAttestations); ii++ {
dst = ssz.WriteOffset(dst, offset)
offset += b.previousEpochAttestations[ii].SizeSSZ()
}
}
for ii := 0; ii < len(b.previousEpochAttestations); ii++ {
if dst, err = b.previousEpochAttestations[ii].MarshalSSZTo(dst); err != nil {
return
}
}
// Field (16) 'currentEpochAttestations'
if len(b.currentEpochAttestations) > 4096 {
err = ssz.ErrListTooBig
return
}
{
offset = 4 * len(b.currentEpochAttestations)
for ii := 0; ii < len(b.currentEpochAttestations); ii++ {
dst = ssz.WriteOffset(dst, offset)
offset += b.currentEpochAttestations[ii].SizeSSZ()
}
}
for ii := 0; ii < len(b.currentEpochAttestations); ii++ {
if dst, err = b.currentEpochAttestations[ii].MarshalSSZTo(dst); err != nil {
return
}
}
return
}
// UnmarshalSSZ ssz unmarshals the BeaconState object
func (b *BeaconState) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 2687377 {
return ssz.ErrSize
}
tail := buf
var o7, o9, o11, o12, o15, o16 uint64
// Field (0) 'genesisTime'
b.genesisTime = ssz.UnmarshallUint64(buf[0:8])
// Field (1) 'genesisValidatorsRoot'
copy(b.genesisValidatorsRoot[:], buf[8:40])
// Field (2) 'slot'
b.slot = eth2types.Slot(ssz.UnmarshallUint64(buf[40:48]))
// Field (3) 'fork'
if b.fork == nil {
b.fork = new(ethpb.Fork)
}
if err = b.fork.UnmarshalSSZ(buf[48:64]); err != nil {
return err
}
// Field (4) 'latestBlockHeader'
if b.latestBlockHeader == nil {
b.latestBlockHeader = new(ethpb.BeaconBlockHeader)
}
if err = b.latestBlockHeader.UnmarshalSSZ(buf[64:176]); err != nil {
return err
}
// Field (5) 'blockRoots'
for ii := 0; ii < 8192; ii++ {
copy(b.blockRoots[ii][:], buf[176:262320][ii*32:(ii+1)*32])
}
// Field (6) 'stateRoots'
for ii := 0; ii < 8192; ii++ {
copy(b.stateRoots[ii][:], buf[262320:524464][ii*32:(ii+1)*32])
}
// Offset (7) 'historicalRoots'
if o7 = ssz.ReadOffset(buf[524464:524468]); o7 > size {
return ssz.ErrOffset
}
if o7 < 2687377 {
return ssz.ErrInvalidVariableOffset
}
// Field (8) 'eth1Data'
if b.eth1Data == nil {
b.eth1Data = new(ethpb.Eth1Data)
}
if err = b.eth1Data.UnmarshalSSZ(buf[524468:524540]); err != nil {
return err
}
// Offset (9) 'eth1DataVotes'
if o9 = ssz.ReadOffset(buf[524540:524544]); o9 > size || o7 > o9 {
return ssz.ErrOffset
}
// Field (10) 'eth1DepositIndex'
b.eth1DepositIndex = ssz.UnmarshallUint64(buf[524544:524552])
// Offset (11) 'validators'
if o11 = ssz.ReadOffset(buf[524552:524556]); o11 > size || o9 > o11 {
return ssz.ErrOffset
}
// Offset (12) 'balances'
if o12 = ssz.ReadOffset(buf[524556:524560]); o12 > size || o11 > o12 {
return ssz.ErrOffset
}
// Field (13) 'randaoMixes'
for ii := 0; ii < 65536; ii++ {
copy(b.randaoMixes[ii][:], buf[524560:2621712][ii*32:(ii+1)*32])
}
// Field (14) 'slashings'
b.slashings = ssz.ExtendUint64(b.slashings, 8192)
for ii := 0; ii < 8192; ii++ {
b.slashings[ii] = ssz.UnmarshallUint64(buf[2621712:2687248][ii*8 : (ii+1)*8])
}
// Offset (15) 'previousEpochAttestations'
if o15 = ssz.ReadOffset(buf[2687248:2687252]); o15 > size || o12 > o15 {
return ssz.ErrOffset
}
// Offset (16) 'currentEpochAttestations'
if o16 = ssz.ReadOffset(buf[2687252:2687256]); o16 > size || o15 > o16 {
return ssz.ErrOffset
}
// Field (17) 'justificationBits'
if cap(b.justificationBits) == 0 {
b.justificationBits = make([]byte, 0, len(buf[2687256:2687257]))
}
b.justificationBits = append(b.justificationBits, buf[2687256:2687257]...)
// Field (18) 'previousJustifiedCheckpoint'
if b.previousJustifiedCheckpoint == nil {
b.previousJustifiedCheckpoint = new(ethpb.Checkpoint)
}
if err = b.previousJustifiedCheckpoint.UnmarshalSSZ(buf[2687257:2687297]); err != nil {
return err
}
// Field (19) 'currentJustifiedCheckpoint'
if b.currentJustifiedCheckpoint == nil {
b.currentJustifiedCheckpoint = new(ethpb.Checkpoint)
}
if err = b.currentJustifiedCheckpoint.UnmarshalSSZ(buf[2687297:2687337]); err != nil {
return err
}
// Field (20) 'finalizedCheckpoint'
if b.finalizedCheckpoint == nil {
b.finalizedCheckpoint = new(ethpb.Checkpoint)
}
if err = b.finalizedCheckpoint.UnmarshalSSZ(buf[2687337:2687377]); err != nil {
return err
}
// Field (7) 'historicalRoots'
{
buf = tail[o7:o9]
num, err := ssz.DivideInt2(len(buf), 32, 16777216)
if err != nil {
return err
}
b.historicalRoots = make([][32]byte, num)
for ii := 0; ii < num; ii++ {
copy(b.historicalRoots[ii][:], buf[ii*32:(ii+1)*32])
}
}
// Field (9) 'eth1DataVotes'
{
buf = tail[o9:o11]
num, err := ssz.DivideInt2(len(buf), 72, 2048)
if err != nil {
return err
}
b.eth1DataVotes = make([]*ethpb.Eth1Data, num)
for ii := 0; ii < num; ii++ {
if b.eth1DataVotes[ii] == nil {
b.eth1DataVotes[ii] = new(ethpb.Eth1Data)
}
if err = b.eth1DataVotes[ii].UnmarshalSSZ(buf[ii*72 : (ii+1)*72]); err != nil {
return err
}
}
}
// Field (11) 'validators'
{
buf = tail[o11:o12]
num, err := ssz.DivideInt2(len(buf), 121, 1099511627776)
if err != nil {
return err
}
b.validators = make([]*ethpb.Validator, num)
for ii := 0; ii < num; ii++ {
if b.validators[ii] == nil {
b.validators[ii] = new(ethpb.Validator)
}
if err = b.validators[ii].UnmarshalSSZ(buf[ii*121 : (ii+1)*121]); err != nil {
return err
}
}
}
// Field (12) 'balances'
{
buf = tail[o12:o15]
num, err := ssz.DivideInt2(len(buf), 8, 1099511627776)
if err != nil {
return err
}
b.balances = ssz.ExtendUint64(b.balances, num)
for ii := 0; ii < num; ii++ {
b.balances[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])
}
}
// Field (15) 'previousEpochAttestations'
{
buf = tail[o15:o16]
num, err := ssz.DecodeDynamicLength(buf, 4096)
if err != nil {
return err
}
b.previousEpochAttestations = make([]*ethpb.PendingAttestation, num)
err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
if b.previousEpochAttestations[indx] == nil {
b.previousEpochAttestations[indx] = new(ethpb.PendingAttestation)
}
if err = b.previousEpochAttestations[indx].UnmarshalSSZ(buf); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
// Field (16) 'currentEpochAttestations'
{
buf = tail[o16:]
num, err := ssz.DecodeDynamicLength(buf, 4096)
if err != nil {
return err
}
b.currentEpochAttestations = make([]*ethpb.PendingAttestation, num)
err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
if b.currentEpochAttestations[indx] == nil {
b.currentEpochAttestations[indx] = new(ethpb.PendingAttestation)
}
if err = b.currentEpochAttestations[indx].UnmarshalSSZ(buf); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the BeaconState object
func (b *BeaconState) SizeSSZ() (size int) {
size = 2687377
// Field (7) 'historicalRoots'
size += len(b.historicalRoots) * 32
// Field (9) 'eth1DataVotes'
size += len(b.eth1DataVotes) * 72
// Field (11) 'validators'
size += len(b.validators) * 121
// Field (12) 'balances'
size += len(b.balances) * 8
// Field (15) 'previousEpochAttestations'
for ii := 0; ii < len(b.previousEpochAttestations); ii++ {
size += 4
size += b.previousEpochAttestations[ii].SizeSSZ()
}
// Field (16) 'currentEpochAttestations'
for ii := 0; ii < len(b.currentEpochAttestations); ii++ {
size += 4
size += b.currentEpochAttestations[ii].SizeSSZ()
}
return
}

View File

@@ -1,46 +0,0 @@
package v1
import (
"testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
atts, err := s.PreviousEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, []*ethpb.PendingAttestation(nil), atts)
want := []*ethpb.PendingAttestation{{ProposerIndex: 100}}
s, err = InitializeFromProto(&ethpb.BeaconState{PreviousEpochAttestations: want})
require.NoError(t, err)
got, err := s.PreviousEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0].ProposerIndex = 101
require.DeepNotEqual(t, want, got)
}
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s, err := InitializeFromProto(&ethpb.BeaconState{})
require.NoError(t, err)
atts, err := s.CurrentEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, []*ethpb.PendingAttestation(nil), atts)
want := []*ethpb.PendingAttestation{{ProposerIndex: 101}}
s, err = InitializeFromProto(&ethpb.BeaconState{CurrentEpochAttestations: want})
require.NoError(t, err)
got, err := s.CurrentEpochAttestations()
require.NoError(t, err)
require.DeepEqual(t, want, got)
// Test copy does not mutate.
got[0].ProposerIndex = 102
require.DeepNotEqual(t, want, got)
}

View File

@@ -1,45 +0,0 @@
package v1
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
func TestBeaconState_LatestBlockHeader(t *testing.T) {
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) {
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) {
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

@@ -1,124 +0,0 @@
package v1
import (
"bytes"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// JustificationBits marking which epochs have been justified in the beacon chain.
func (b *BeaconState) JustificationBits() bitfield.Bitvector4 {
if b.justificationBits == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.justificationBitsVal()
}
// justificationBitsVal marking which epochs have been justified in the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) justificationBitsVal() bitfield.Bitvector4 {
if b.justificationBits == nil {
return nil
}
res := make([]byte, len(b.justificationBits.Bytes()))
copy(res, b.justificationBits.Bytes())
return res
}
// PreviousJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) PreviousJustifiedCheckpoint() *ethpb.Checkpoint {
if b.previousJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.previousJustifiedCheckpointVal()
}
// previousJustifiedCheckpointVal denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) previousJustifiedCheckpointVal() *ethpb.Checkpoint {
return ethpb.CopyCheckpoint(b.previousJustifiedCheckpoint)
}
// CurrentJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) CurrentJustifiedCheckpoint() *ethpb.Checkpoint {
if b.currentJustifiedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.currentJustifiedCheckpointVal()
}
// currentJustifiedCheckpointVal denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) currentJustifiedCheckpointVal() *ethpb.Checkpoint {
return ethpb.CopyCheckpoint(b.currentJustifiedCheckpoint)
}
// MatchCurrentJustifiedCheckpoint returns true if input justified checkpoint matches
// the current justified checkpoint in state.
func (b *BeaconState) MatchCurrentJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if b.currentJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.currentJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.currentJustifiedCheckpoint.Root)
}
// MatchPreviousJustifiedCheckpoint returns true if the input justified checkpoint matches
// the previous justified checkpoint in state.
func (b *BeaconState) MatchPreviousJustifiedCheckpoint(c *ethpb.Checkpoint) bool {
if b.previousJustifiedCheckpoint == nil {
return false
}
if c.Epoch != b.previousJustifiedCheckpoint.Epoch {
return false
}
return bytes.Equal(c.Root, b.previousJustifiedCheckpoint.Root)
}
// FinalizedCheckpoint denoting an epoch and block root.
func (b *BeaconState) FinalizedCheckpoint() *ethpb.Checkpoint {
if b.finalizedCheckpoint == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.finalizedCheckpointVal()
}
// finalizedCheckpointVal denoting an epoch and block root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) finalizedCheckpointVal() *ethpb.Checkpoint {
return ethpb.CopyCheckpoint(b.finalizedCheckpoint)
}
// FinalizedCheckpointEpoch returns the epoch value of the finalized checkpoint.
func (b *BeaconState) FinalizedCheckpointEpoch() types.Epoch {
if b.finalizedCheckpoint == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.finalizedCheckpoint.Epoch
}

View File

@@ -1,74 +0,0 @@
package v1
import (
"testing"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
func TestBeaconState_JustificationBitsNil(t *testing.T) {
testtmpl.VerifyBeaconStateJustificationBitsNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{})
})
}
func TestBeaconState_JustificationBits(t *testing.T) {
testtmpl.VerifyBeaconStateJustificationBits(
t,
func(bits bitfield.Bitvector4) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{JustificationBits: bits})
})
}
func TestBeaconState_PreviousJustifiedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{})
})
}
func TestBeaconState_PreviousJustifiedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{PreviousJustifiedCheckpoint: cp})
})
}
func TestBeaconState_CurrentJustifiedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{})
})
}
func TestBeaconState_CurrentJustifiedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{CurrentJustifiedCheckpoint: cp})
})
}
func TestBeaconState_FinalizedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStateFinalizedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{})
})
}
func TestBeaconState_FinalizedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStateFinalizedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconState{FinalizedCheckpoint: cp})
})
}

View File

@@ -1,64 +0,0 @@
package v1
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// Eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
func (b *BeaconState) Eth1Data() *ethpb.Eth1Data {
if b.eth1Data == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DataVal()
}
// eth1DataVal corresponding to the proof-of-work chain information stored in the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DataVal() *ethpb.Eth1Data {
if b.eth1Data == nil {
return nil
}
return ethpb.CopyETH1Data(b.eth1Data)
}
// Eth1DataVotes corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
func (b *BeaconState) Eth1DataVotes() []*ethpb.Eth1Data {
if b.eth1DataVotes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DataVotesVal()
}
// eth1DataVotesVal corresponds to votes from Ethereum on the canonical proof-of-work chain
// data retrieved from eth1.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) eth1DataVotesVal() []*ethpb.Eth1Data {
if b.eth1DataVotes == nil {
return nil
}
res := make([]*ethpb.Eth1Data, len(b.eth1DataVotes))
for i := 0; i < len(res); i++ {
res[i] = ethpb.CopyETH1Data(b.eth1DataVotes[i])
}
return res
}
// Eth1DepositIndex corresponds to the index of the deposit made to the
// validator deposit contract at the time of this state's eth1 data.
func (b *BeaconState) Eth1DepositIndex() uint64 {
b.lock.RLock()
defer b.lock.RUnlock()
return b.eth1DepositIndex
}

View File

@@ -1,66 +0,0 @@
package v1
import (
"fmt"
)
// RandaoMixes of block proposers on the beacon chain.
func (b *BeaconState) RandaoMixes() [][]byte {
if b.randaoMixes == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixes.Slice()
}
// RandaoMixAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) {
if b.randaoMixes == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
m, err := b.randaoMixAtIndex(idx)
if err != nil {
return nil, err
}
return m[:], nil
}
// randaoMixAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixAtIndex(idx uint64) ([32]byte, error) {
if uint64(len(b.randaoMixes)) <= idx {
return [32]byte{}, fmt.Errorf("index %d out of range", idx)
}
return b.randaoMixes[idx], nil
}
// RandaoMixesLength returns the length of the randao mixes slice.
func (b *BeaconState) RandaoMixesLength() int {
if b.randaoMixes == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.randaoMixesLength()
}
// randaoMixesLength returns the length of the randao mixes slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) randaoMixesLength() int {
if b.randaoMixes == nil {
return 0
}
return len(b.randaoMixes)
}

View File

@@ -1,39 +0,0 @@
package v1
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
func TestBeaconState_SlotDataRace(t *testing.T) {
testtmpl.VerifyBeaconStateSlotDataRace(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{Slot: 1})
})
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{CurrentJustifiedCheckpoint: cp})
},
)
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{PreviousJustifiedCheckpoint: cp})
},
)
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconState{})
})
}

View File

@@ -1,250 +0,0 @@
package v1
import (
"fmt"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"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"
)
// ValidatorIndexOutOfRangeError represents an error scenario where a validator does not exist
// at a given index in the validator's array.
type ValidatorIndexOutOfRangeError struct {
message string
}
// NewValidatorIndexOutOfRangeError creates a new error instance.
func NewValidatorIndexOutOfRangeError(index types.ValidatorIndex) ValidatorIndexOutOfRangeError {
return ValidatorIndexOutOfRangeError{
message: fmt.Sprintf("index %d out of range", index),
}
}
// Error returns the underlying error message.
func (e *ValidatorIndexOutOfRangeError) Error() string {
return e.message
}
// Validators participating in consensus on the beacon chain.
func (b *BeaconState) Validators() []*ethpb.Validator {
if b.validators == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.validatorsVal()
}
// validatorsVal participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) validatorsVal() []*ethpb.Validator {
if b.validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.validators))
for i := 0; i < len(res); i++ {
val := b.validators[i]
if val == nil {
continue
}
res[i] = ethpb.CopyValidator(val)
}
return res
}
// references of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState. This does not
// copy fully and instead just copies the reference.
func (b *BeaconState) validatorsReferences() []*ethpb.Validator {
if b.validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.validators))
for i := 0; i < len(res); i++ {
validator := b.validators[i]
if validator == nil {
continue
}
// copy validator reference instead.
res[i] = validator
}
return res
}
// ValidatorAtIndex is the validator at the provided index.
func (b *BeaconState) ValidatorAtIndex(idx types.ValidatorIndex) (*ethpb.Validator, error) {
if b.validators == nil {
return &ethpb.Validator{}, nil
}
if uint64(len(b.validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
val := b.validators[idx]
return ethpb.CopyValidator(val), nil
}
// ValidatorAtIndexReadOnly is the validator at the provided index. This method
// doesn't clone the validator.
func (b *BeaconState) ValidatorAtIndexReadOnly(idx types.ValidatorIndex) (state.ReadOnlyValidator, error) {
if b.validators == nil {
return nil, state.ErrNilValidatorsInState
}
if uint64(len(b.validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
return NewValidator(b.validators[idx])
}
// ValidatorIndexByPubkey returns a given validator by its 48-byte public key.
func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool) {
if b == nil || b.valMapHandler == nil || b.valMapHandler.IsNil() {
return 0, false
}
b.lock.RLock()
defer b.lock.RUnlock()
numOfVals := len(b.validators)
idx, ok := b.valMapHandler.Get(key)
if ok && types.ValidatorIndex(numOfVals) <= idx {
return types.ValidatorIndex(0), false
}
return idx, ok
}
// PubkeyAtIndex returns the pubkey at the given
// validator index.
func (b *BeaconState) PubkeyAtIndex(idx types.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte {
if uint64(idx) >= uint64(len(b.validators)) {
return [fieldparams.BLSPubkeyLength]byte{}
}
b.lock.RLock()
defer b.lock.RUnlock()
if b.validators[idx] == nil {
return [fieldparams.BLSPubkeyLength]byte{}
}
return bytesutil.ToBytes48(b.validators[idx].PublicKey)
}
// NumValidators returns the size of the validator registry.
func (b *BeaconState) NumValidators() int {
b.lock.RLock()
defer b.lock.RUnlock()
return len(b.validators)
}
// ReadFromEveryValidator reads values from every validator and applies it to the provided function.
// Warning: This method is potentially unsafe, as it exposes the actual validator registry.
func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
if b.validators == nil {
return errors.New("nil validators in state")
}
b.lock.RLock()
validators := b.validators
b.lock.RUnlock()
for i, v := range validators {
v, err := NewValidator(v)
if err != nil {
return err
}
if err := f(i, v); err != nil {
return err
}
}
return nil
}
// Balances of validators participating in consensus on the beacon chain.
func (b *BeaconState) Balances() []uint64 {
if b.balances == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balancesVal()
}
// balances of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balancesVal() []uint64 {
if b.balances == nil {
return nil
}
res := make([]uint64, len(b.balances))
copy(res, b.balances)
return res
}
// BalanceAtIndex of validator with the provided index.
func (b *BeaconState) BalanceAtIndex(idx types.ValidatorIndex) (uint64, error) {
if b.balances == nil {
return 0, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
if uint64(len(b.balances)) <= uint64(idx) {
return 0, fmt.Errorf("index of %d does not exist", idx)
}
return b.balances[idx], nil
}
// BalancesLength returns the length of the balances slice.
func (b *BeaconState) BalancesLength() int {
if b.balances == nil {
return 0
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.balancesLength()
}
// Slashings of validators on the beacon chain.
func (b *BeaconState) Slashings() []uint64 {
if b.slashings == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.slashingsVal()
}
// slashings of validators on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) slashingsVal() []uint64 {
if b.slashings == nil {
return nil
}
res := make([]uint64, len(b.slashings))
copy(res, b.slashings)
return res
}

View File

@@ -1,18 +0,0 @@
package v1_test
import (
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
testtmpl "github.com/prysmaticlabs/prysm/beacon-chain/state/testing"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice(t *testing.T) {
testtmpl.VerifyBeaconStateValidatorAtIndexReadOnlyHandlesNilSlice(t, func() (state.BeaconState, error) {
return v1.InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: nil,
})
})
}

View File

@@ -1,64 +0,0 @@
package v1_test
import (
"context"
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
"github.com/prysmaticlabs/prysm/container/trie"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBeaconStateMerkleProofs(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisState(t, 256)
htr, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("current sync committee", func(t *testing.T) {
_, err := st.CurrentSyncCommitteeProof(ctx)
require.ErrorContains(t, "unsupported", err)
})
t.Run("next sync committee", func(t *testing.T) {
_, err := st.NextSyncCommitteeProof(ctx)
require.ErrorContains(t, "unsupported", err)
})
t.Run("finalized root", func(t *testing.T) {
finalizedRoot := st.FinalizedCheckpoint().Root
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
gIndex := v1.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(htr[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
t.Run("recomputes root on dirty fields", func(t *testing.T) {
currentRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
cpt := st.FinalizedCheckpoint()
require.NoError(t, err)
// Edit the checkpoint.
cpt.Epoch = 100
require.NoError(t, st.SetFinalizedCheckpoint(cpt))
// Produce a proof for the finalized root.
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
// We expect the previous step to have triggered
// a recomputation of dirty fields in the beacon state, resulting
// in a new hash tree root as the finalized checkpoint had previously
// changed and should have been marked as a dirty state field.
// The proof validity should be false for the old root, but true for the new.
finalizedRoot := st.FinalizedCheckpoint().Root
gIndex := v1.FinalizedRootGeneralizedIndex()
valid := trie.VerifyMerkleProof(currentRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, false, valid)
newRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
valid = trie.VerifyMerkleProof(newRoot[:], finalizedRoot, gIndex, proof)
require.Equal(t, true, valid)
})
}

View File

@@ -1,74 +0,0 @@
package v1_test
import (
"testing"
types "github.com/prysmaticlabs/eth2-types"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestReadOnlyValidator_ReturnsErrorOnNil(t *testing.T) {
if _, err := v1.NewValidator(nil); err != v1.ErrNilWrappedValidator {
t.Errorf("Wrong error returned. Got %v, wanted %v", err, v1.ErrNilWrappedValidator)
}
}
func TestReadOnlyValidator_EffectiveBalance(t *testing.T) {
bal := uint64(234)
v, err := v1.NewValidator(&ethpb.Validator{EffectiveBalance: bal})
require.NoError(t, err)
assert.Equal(t, bal, v.EffectiveBalance())
}
func TestReadOnlyValidator_ActivationEligibilityEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ActivationEligibilityEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ActivationEligibilityEpoch())
}
func TestReadOnlyValidator_ActivationEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ActivationEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ActivationEpoch())
}
func TestReadOnlyValidator_WithdrawableEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{WithdrawableEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.WithdrawableEpoch())
}
func TestReadOnlyValidator_ExitEpoch(t *testing.T) {
epoch := types.Epoch(234)
v, err := v1.NewValidator(&ethpb.Validator{ExitEpoch: epoch})
require.NoError(t, err)
assert.Equal(t, epoch, v.ExitEpoch())
}
func TestReadOnlyValidator_PublicKey(t *testing.T) {
key := [fieldparams.BLSPubkeyLength]byte{0xFA, 0xCC}
v, err := v1.NewValidator(&ethpb.Validator{PublicKey: key[:]})
require.NoError(t, err)
assert.Equal(t, key, v.PublicKey())
}
func TestReadOnlyValidator_WithdrawalCredentials(t *testing.T) {
creds := []byte{0xFA, 0xCC}
v, err := v1.NewValidator(&ethpb.Validator{WithdrawalCredentials: creds})
require.NoError(t, err)
assert.DeepEqual(t, creds, v.WithdrawalCredentials())
}
func TestReadOnlyValidator_Slashed(t *testing.T) {
slashed := true
v, err := v1.NewValidator(&ethpb.Validator{Slashed: slashed})
require.NoError(t, err)
assert.Equal(t, slashed, v.Slashed())
}

View File

@@ -1,393 +0,0 @@
package v1
import (
"reflect"
"runtime"
"runtime/debug"
"testing"
"github.com/prysmaticlabs/go-bitfield"
"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.BeaconState{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.BeaconState{
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.BeaconState{
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 TestStateReferenceCopy_NoUnexpectedAttestationsMutation(t *testing.T) {
assertAttFound := func(vals []*ethpb.PendingAttestation, val uint64) {
for i := range vals {
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
return
}
}
t.Log(string(debug.Stack()))
t.Fatalf("Expected attestation not found (%v), want: %v", vals, val)
}
assertAttNotFound := func(vals []*ethpb.PendingAttestation, val uint64) {
for i := range vals {
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
t.Log(string(debug.Stack()))
t.Fatalf("Unexpected attestation found (%v): %v", vals, val)
return
}
}
}
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{})
require.NoError(t, err)
s, ok := a.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, previousEpochAttestations, 1)
assertRefCount(t, s, currentEpochAttestations, 1)
// Update initial state.
atts := []*ethpb.PendingAttestation{
{AggregationBits: bitfield.NewBitlist(1)},
{AggregationBits: bitfield.NewBitlist(2)},
}
s.setPreviousEpochAttestations(atts[:1])
s.setCurrentEpochAttestations(atts[:1])
curAtt, err := a.CurrentEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 1, len(curAtt), "Unexpected number of attestations")
preAtt, err := a.PreviousEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 1, len(preAtt), "Unexpected number of attestations")
// Copy, increases reference count.
copied := a.Copy()
b, ok := copied.(*BeaconState)
require.Equal(t, true, ok)
assertRefCount(t, s, previousEpochAttestations, 2)
assertRefCount(t, s, currentEpochAttestations, 2)
assertRefCount(t, b, previousEpochAttestations, 2)
assertRefCount(t, b, currentEpochAttestations, 2)
bPrevEpochAtts, err := b.PreviousEpochAttestations()
require.NoError(t, err)
bCurrEpochAtts, err := b.CurrentEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 1, len(bPrevEpochAtts), "Unexpected number of attestations")
assert.Equal(t, 1, len(bCurrEpochAtts), "Unexpected number of attestations")
// Assert shared state.
aCurrEpochAtts, err := a.CurrentEpochAttestations()
require.NoError(t, err)
curAttsA := aCurrEpochAtts
aPrevEpochAtts, err := a.PreviousEpochAttestations()
require.NoError(t, err)
prevAttsA := aPrevEpochAtts
bCurrEpochAtts, err = b.CurrentEpochAttestations()
require.NoError(t, err)
curAttsB := bCurrEpochAtts
bPrevEpochAtts, err = b.PreviousEpochAttestations()
require.NoError(t, err)
prevAttsB := bPrevEpochAtts
if len(curAttsA) != len(curAttsB) || len(curAttsA) < 1 {
t.Errorf("Unexpected number of attestations, want: %v", 1)
}
if len(prevAttsA) != len(prevAttsB) || len(prevAttsA) < 1 {
t.Errorf("Unexpected number of attestations, want: %v", 1)
}
assertAttFound(curAttsA, 1)
assertAttFound(prevAttsA, 1)
assertAttFound(curAttsB, 1)
assertAttFound(prevAttsB, 1)
// Extends state a attestations.
require.NoError(t, a.AppendCurrentEpochAttestations(atts[1]))
require.NoError(t, a.AppendPreviousEpochAttestations(atts[1]))
curAtt, err = a.CurrentEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 2, len(curAtt), "Unexpected number of attestations")
preAtt, err = a.PreviousEpochAttestations()
require.NoError(t, err)
assert.Equal(t, 2, len(preAtt), "Unexpected number of attestations")
aCurrEpochAtts, err = a.CurrentEpochAttestations()
require.NoError(t, err)
aPrevEpochAtts, err = a.PreviousEpochAttestations()
require.NoError(t, err)
bCurrEpochAtts, err = b.CurrentEpochAttestations()
require.NoError(t, err)
bPrevEpochAtts, err = b.PreviousEpochAttestations()
require.NoError(t, err)
assertAttFound(aCurrEpochAtts, 1)
assertAttFound(aPrevEpochAtts, 1)
assertAttFound(aCurrEpochAtts, 2)
assertAttFound(aPrevEpochAtts, 2)
assertAttFound(bCurrEpochAtts, 1)
assertAttFound(bPrevEpochAtts, 1)
assertAttNotFound(bCurrEpochAtts, 2)
assertAttNotFound(bPrevEpochAtts, 2)
// Mutator should only affect calling state: a.
applyToEveryAttestation := func(state *BeaconState) {
// One MUST copy on write.
atts = make([]*ethpb.PendingAttestation, len(state.currentEpochAttestations))
copy(atts, state.currentEpochAttestations)
state.currentEpochAttestations = atts
currEpochAtts, err := state.CurrentEpochAttestations()
require.NoError(t, err)
for i := range currEpochAtts {
att := ethpb.CopyPendingAttestation(state.currentEpochAttestations[i])
att.AggregationBits = bitfield.NewBitlist(3)
state.currentEpochAttestations[i] = att
}
atts = make([]*ethpb.PendingAttestation, len(state.previousEpochAttestations))
copy(atts, state.previousEpochAttestations)
state.previousEpochAttestations = atts
prevEpochAtts, err := state.PreviousEpochAttestations()
require.NoError(t, err)
for i := range prevEpochAtts {
att := ethpb.CopyPendingAttestation(state.previousEpochAttestations[i])
att.AggregationBits = bitfield.NewBitlist(3)
state.previousEpochAttestations[i] = att
}
}
applyToEveryAttestation(s)
aCurrEpochAtts, err = a.CurrentEpochAttestations()
require.NoError(t, err)
aPrevEpochAtts, err = a.PreviousEpochAttestations()
require.NoError(t, err)
bCurrEpochAtts, err = b.CurrentEpochAttestations()
require.NoError(t, err)
bPrevEpochAtts, err = b.PreviousEpochAttestations()
require.NoError(t, err)
// Assert no shared state mutation occurred only on state a (copy on write).
assertAttFound(aCurrEpochAtts, 3)
assertAttFound(aPrevEpochAtts, 3)
assertAttNotFound(aCurrEpochAtts, 1)
assertAttNotFound(aPrevEpochAtts, 1)
assertAttNotFound(aCurrEpochAtts, 2)
assertAttNotFound(aPrevEpochAtts, 2)
// State b must be unaffected.
assertAttNotFound(bCurrEpochAtts, 3)
assertAttNotFound(bPrevEpochAtts, 3)
assertAttFound(bCurrEpochAtts, 1)
assertAttFound(bPrevEpochAtts, 1)
assertAttNotFound(bCurrEpochAtts, 2)
assertAttNotFound(bPrevEpochAtts, 2)
// Copy on write happened, reference counters are reset.
assertRefCount(t, s, currentEpochAttestations, 1)
assertRefCount(t, b, currentEpochAttestations, 1)
assertRefCount(t, s, previousEpochAttestations, 1)
assertRefCount(t, b, previousEpochAttestations, 1)
}
func TestValidatorReferences_RemainsConsistent(t *testing.T) {
a, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
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

@@ -1,26 +0,0 @@
package v1
import (
"testing"
types "github.com/prysmaticlabs/eth2-types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_RotateAttestations(t *testing.T) {
st, err := InitializeFromProto(&ethpb.BeaconState{
Slot: 1,
CurrentEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 456}}},
PreviousEpochAttestations: []*ethpb.PendingAttestation{{Data: &ethpb.AttestationData{Slot: 123}}},
})
require.NoError(t, err)
require.NoError(t, st.RotateAttestations())
currEpochAtts, err := st.CurrentEpochAttestations()
require.NoError(t, err)
require.Equal(t, 0, len(currEpochAtts))
prevEpochAtts, err := st.PreviousEpochAttestations()
require.NoError(t, err)
require.Equal(t, types.Slot(456), prevEpochAtts[0].Data.Slot)
}

View File

@@ -1,46 +0,0 @@
package v1
import (
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetJustificationBits for the beacon state.
func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
b.lock.Lock()
defer b.lock.Unlock()
b.justificationBits = val
b.markFieldAsDirty(justificationBits)
return nil
}
// SetPreviousJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) error {
b.lock.Lock()
defer b.lock.Unlock()
b.previousJustifiedCheckpoint = val
b.markFieldAsDirty(previousJustifiedCheckpoint)
return nil
}
// SetCurrentJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error {
b.lock.Lock()
defer b.lock.Unlock()
b.currentJustifiedCheckpoint = val
b.markFieldAsDirty(currentJustifiedCheckpoint)
return nil
}
// SetFinalizedCheckpoint for the beacon state.
func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
b.lock.Lock()
defer b.lock.Unlock()
b.finalizedCheckpoint = val
b.markFieldAsDirty(finalizedCheckpoint)
return nil
}

View File

@@ -1,62 +0,0 @@
package v1
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetEth1Data for the beacon state.
func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()
b.eth1Data = val
b.markFieldAsDirty(eth1Data)
return nil
}
// SetEth1DataVotes for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.eth1DataVotes = val
b.markFieldAsDirty(eth1DataVotes)
b.rebuildTrie[eth1DataVotes] = true
return nil
}
// SetEth1DepositIndex for the beacon state.
func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.eth1DepositIndex = val
b.markFieldAsDirty(eth1DepositIndex)
return nil
}
// AppendEth1DataVotes for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()
votes := b.eth1DataVotes
if b.sharedFieldReferences[eth1DataVotes].Refs() > 1 {
// Copy elements in underlying array by reference.
votes = make([]*ethpb.Eth1Data, len(b.eth1DataVotes))
copy(votes, b.eth1DataVotes)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
}
b.eth1DataVotes = append(votes, val)
b.markFieldAsDirty(eth1DataVotes)
b.addDirtyIndices(eth1DataVotes, []uint64{uint64(len(b.eth1DataVotes) - 1)})
return nil
}

View File

@@ -1,201 +0,0 @@
package v1
import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetValidators for the beacon state. Updates the entire
// to a new value by overwriting the previous one.
func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
b.lock.Lock()
defer b.lock.Unlock()
b.validators = val
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.markFieldAsDirty(validators)
b.rebuildTrie[validators] = true
b.valMapHandler = stateutil.NewValMapHandler(b.validators)
return nil
}
// ApplyToEveryValidator applies the provided callback function to each validator in the
// validator registry.
func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error {
b.lock.Lock()
v := b.validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
b.lock.Unlock()
var changedVals []uint64
for i, val := range v {
changed, newVal, err := f(i, val)
if err != nil {
return err
}
if changed {
changedVals = append(changedVals, uint64(i))
v[i] = newVal
}
}
b.lock.Lock()
defer b.lock.Unlock()
b.validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, changedVals)
return nil
}
// UpdateValidatorAtIndex for the beacon state. Updates the validator
// at a specific index to a new value.
func (b *BeaconState) UpdateValidatorAtIndex(idx types.ValidatorIndex, val *ethpb.Validator) error {
if uint64(len(b.validators)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
v := b.validators
if ref := b.sharedFieldReferences[validators]; ref.Refs() > 1 {
v = b.validatorsReferences()
ref.MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
v[idx] = val
b.validators = v
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(idx)})
return nil
}
// SetBalances for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBalances(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.balances = val
b.markFieldAsDirty(balances)
b.rebuildTrie[balances] = true
return nil
}
// UpdateBalancesAtIndex for the beacon state. This method updates the balance
// at a specific index to a new value.
func (b *BeaconState) UpdateBalancesAtIndex(idx types.ValidatorIndex, val uint64) error {
if uint64(len(b.balances)) <= uint64(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
bals := b.balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balancesVal()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
bals[idx] = val
b.balances = bals
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(idx)})
return nil
}
// SetSlashings for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetSlashings(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.slashings = val
b.markFieldAsDirty(slashings)
return nil
}
// UpdateSlashingsAtIndex for the beacon state. Updates the slashings
// at a specific index to a new value.
func (b *BeaconState) UpdateSlashingsAtIndex(idx, val uint64) error {
if uint64(len(b.slashings)) <= idx {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
s := b.slashings
if b.sharedFieldReferences[slashings].Refs() > 1 {
s = b.slashingsVal()
b.sharedFieldReferences[slashings].MinusRef()
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
}
s[idx] = val
b.slashings = s
b.markFieldAsDirty(slashings)
return nil
}
// AppendValidator for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
b.lock.Lock()
defer b.lock.Unlock()
vals := b.validators
if b.sharedFieldReferences[validators].Refs() > 1 {
vals = b.validatorsReferences()
b.sharedFieldReferences[validators].MinusRef()
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
}
// append validator to slice
b.validators = append(vals, val)
valIdx := types.ValidatorIndex(len(b.validators) - 1)
b.valMapHandler.Set(bytesutil.ToBytes48(val.PublicKey), valIdx)
b.markFieldAsDirty(validators)
b.addDirtyIndices(validators, []uint64{uint64(valIdx)})
return nil
}
// AppendBalance for the beacon state. Appends the new value
// to the the end of list.
func (b *BeaconState) AppendBalance(bal uint64) error {
b.lock.Lock()
defer b.lock.Unlock()
bals := b.balances
if b.sharedFieldReferences[balances].Refs() > 1 {
bals = b.balancesVal()
b.sharedFieldReferences[balances].MinusRef()
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
}
b.balances = append(bals, bal)
balIdx := len(b.balances) - 1
b.markFieldAsDirty(balances)
b.addDirtyIndices(balances, []uint64{uint64(balIdx)})
return nil
}

View File

@@ -1,208 +0,0 @@
package v1
import (
"context"
"strconv"
"sync"
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"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 TestValidatorMap_DistinctCopy(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
handler := stateutil.NewValMapHandler(vals)
newHandler := handler.Copy()
wantedPubkey := strconv.Itoa(22)
handler.Set(bytesutil.ToBytes48([]byte(wantedPubkey)), 27)
val1, _ := handler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
val2, _ := newHandler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
assert.NotEqual(t, val1, val2, "Values are supposed to be unequal due to copy")
}
func TestBeaconState_NoDeadlock(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
st, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{
Validators: vals,
})
assert.NoError(t, err)
s, ok := st.(*BeaconState)
require.Equal(t, true, ok)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
// Continuously lock and unlock the state
// by acquiring the lock.
for i := 0; i < 1000; i++ {
for _, f := range s.stateFieldLeaves {
f.Lock()
if f.Empty() {
f.InsertFieldLayer(make([][]*[32]byte, 10))
}
f.Unlock()
f.FieldReference().AddRef()
}
}
wg.Done()
}()
// Constantly read from the offending portion
// of the code to ensure there is no possible
// recursive read locking.
for i := 0; i < 1000; i++ {
go func() {
_ = st.FieldReferencesCount()
}()
}
// Test will not terminate in the event of a deadlock.
wg.Wait()
}
func TestStateTrie_IsNil(t *testing.T) {
var emptyState *BeaconState
assert.Equal(t, true, emptyState.IsNil())
}
func TestBeaconState_AppendBalanceWithTrie(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
bals := make([]uint64, 0, count)
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [fieldparams.BLSPubkeyLength]byte{}
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
bals = append(bals, params.BeaconConfig().MaxEffectiveBalance)
}
zeroHash := params.BeaconConfig().ZeroHash
mockblockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockblockRoots); i++ {
mockblockRoots[i] = zeroHash[:]
}
mockstateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
for i := 0; i < len(mockstateRoots); i++ {
mockstateRoots[i] = zeroHash[:]
}
mockrandaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
for i := 0; i < len(mockrandaoMixes); i++ {
mockrandaoMixes[i] = zeroHash[:]
}
st, err := InitializeFromProto(&ethpb.BeaconState{
Slot: 1,
GenesisValidatorsRoot: make([]byte, 32),
Fork: &ethpb.Fork{
PreviousVersion: make([]byte, 4),
CurrentVersion: make([]byte, 4),
Epoch: 0,
},
LatestBlockHeader: &ethpb.BeaconBlockHeader{
ParentRoot: make([]byte, fieldparams.RootLength),
StateRoot: make([]byte, fieldparams.RootLength),
BodyRoot: make([]byte, fieldparams.RootLength),
},
Validators: vals,
Balances: bals,
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, 32),
BlockHash: make([]byte, 32),
},
BlockRoots: mockblockRoots,
StateRoots: mockstateRoots,
RandaoMixes: mockrandaoMixes,
JustificationBits: bitfield.NewBitvector4(),
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
})
assert.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
for i := 0; i < 100; i++ {
if i%2 == 0 {
assert.NoError(t, st.UpdateBalancesAtIndex(types.ValidatorIndex(i), 1000))
}
if i%3 == 0 {
assert.NoError(t, st.AppendBalance(1000))
}
}
_, err = st.HashTreeRoot(context.Background())
assert.NoError(t, err)
s, ok := st.(*BeaconState)
require.Equal(t, true, ok)
newRt := bytesutil.ToBytes32(s.merkleLayers[0][balances])
wantedRt, err := stateutil.Uint64ListRootWithRegistryLimit(s.Balances())
assert.NoError(t, err)
assert.Equal(t, wantedRt, newRt, "state roots are unequal")
}
func TestBeaconState_ModifyPreviousParticipationBits(t *testing.T) {
st, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{})
assert.NoError(t, err)
assert.ErrorContains(t, "ModifyPreviousParticipationBits is not supported for phase 0 beacon state", st.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
return nil, nil
}))
}
func TestBeaconState_ModifyCurrentParticipationBits(t *testing.T) {
st, err := InitializeFromProtoUnsafe(&ethpb.BeaconState{})
assert.NoError(t, err)
assert.ErrorContains(t, "ModifyCurrentParticipationBits is not supported for phase 0 beacon state", st.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
return nil, nil
}))
}

View File

@@ -1,479 +0,0 @@
package v1
import (
"context"
"runtime"
"sort"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/container/slice"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
"google.golang.org/protobuf/proto"
)
// InitializeFromProto the beacon state from a protobuf representation.
func InitializeFromProto(st *ethpb.BeaconState) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconState))
}
// InitializeFromProtoUnsafe directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafe(st *ethpb.BeaconState) (state.BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
var bRoots customtypes.BlockRoots
for i, r := range st.BlockRoots {
copy(bRoots[i][:], r)
}
var sRoots customtypes.StateRoots
for i, r := range st.StateRoots {
copy(sRoots[i][:], r)
}
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
for i, r := range st.HistoricalRoots {
copy(hRoots[i][:], r)
}
var mixes customtypes.RandaoMixes
for i, m := range st.RandaoMixes {
copy(mixes[i][:], m)
}
fieldCount := params.BeaconConfig().BeaconStateFieldCount
b := &BeaconState{
genesisTime: st.GenesisTime,
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
slot: st.Slot,
fork: st.Fork,
latestBlockHeader: st.LatestBlockHeader,
blockRoots: &bRoots,
stateRoots: &sRoots,
historicalRoots: hRoots,
eth1Data: st.Eth1Data,
eth1DataVotes: st.Eth1DataVotes,
eth1DepositIndex: st.Eth1DepositIndex,
validators: st.Validators,
balances: st.Balances,
randaoMixes: &mixes,
slashings: st.Slashings,
previousEpochAttestations: st.PreviousEpochAttestations,
currentEpochAttestations: st.CurrentEpochAttestations,
justificationBits: st.JustificationBits,
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
finalizedCheckpoint: st.FinalizedCheckpoint,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 10),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
var err error
for i := 0; i < fieldCount; i++ {
b.dirtyFields[types.FieldIndex(i)] = true
b.rebuildTrie[types.FieldIndex(i)] = true
b.dirtyIndices[types.FieldIndex(i)] = []uint64{}
b.stateFieldLeaves[types.FieldIndex(i)], err = fieldtrie.NewFieldTrie(types.FieldIndex(i), types.BasicArray, nil, 0)
if err != nil {
return nil, err
}
}
// Initialize field reference tracking for shared data.
b.sharedFieldReferences[randaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[previousEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[currentEpochAttestations] = stateutil.NewRef(1)
b.sharedFieldReferences[slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[validators] = stateutil.NewRef(1)
b.sharedFieldReferences[balances] = stateutil.NewRef(1)
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
state.StateCount.Inc()
return b, nil
}
// Copy returns a deep copy of the beacon state.
func (b *BeaconState) Copy() state.BeaconState {
b.lock.RLock()
defer b.lock.RUnlock()
fieldCount := params.BeaconConfig().BeaconStateFieldCount
dst := &BeaconState{
// Primitive types, safe to copy.
genesisTime: b.genesisTime,
slot: b.slot,
eth1DepositIndex: b.eth1DepositIndex,
// Large arrays, infrequently changed, constant size.
slashings: b.slashings,
// Large arrays, infrequently changed, constant size.
blockRoots: b.blockRoots,
stateRoots: b.stateRoots,
randaoMixes: b.randaoMixes,
previousEpochAttestations: b.previousEpochAttestations,
currentEpochAttestations: b.currentEpochAttestations,
eth1DataVotes: b.eth1DataVotes,
// Large arrays, increases over time.
balances: b.balances,
historicalRoots: b.historicalRoots,
validators: b.validators,
// Everything else, too small to be concerned about, constant size.
genesisValidatorsRoot: b.genesisValidatorsRoot,
justificationBits: b.justificationBitsVal(),
fork: b.forkVal(),
latestBlockHeader: b.latestBlockHeaderVal(),
eth1Data: b.eth1DataVal(),
previousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
currentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
finalizedCheckpoint: b.finalizedCheckpointVal(),
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, 10),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
// Share the reference to validator index map.
valMapHandler: b.valMapHandler,
}
for field, ref := range b.sharedFieldReferences {
ref.AddRef()
dst.sharedFieldReferences[field] = ref
}
// Increment ref for validator map
b.valMapHandler.AddRef()
for i := range b.dirtyFields {
dst.dirtyFields[i] = true
}
for i := range b.dirtyIndices {
indices := make([]uint64, len(b.dirtyIndices[i]))
copy(indices, b.dirtyIndices[i])
dst.dirtyIndices[i] = indices
}
for i := range b.rebuildTrie {
dst.rebuildTrie[i] = true
}
for fldIdx, fieldTrie := range b.stateFieldLeaves {
dst.stateFieldLeaves[fldIdx] = fieldTrie
if fieldTrie.FieldReference() != nil {
fieldTrie.Lock()
fieldTrie.FieldReference().AddRef()
fieldTrie.Unlock()
}
}
if b.merkleLayers != nil {
dst.merkleLayers = make([][][]byte, len(b.merkleLayers))
for i, layer := range b.merkleLayers {
dst.merkleLayers[i] = make([][]byte, len(layer))
for j, content := range layer {
dst.merkleLayers[i][j] = make([]byte, len(content))
copy(dst.merkleLayers[i][j], content)
}
}
}
state.StateCount.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(dst, func(b *BeaconState) {
for field, v := range b.sharedFieldReferences {
v.MinusRef()
if b.stateFieldLeaves[field].FieldReference() != nil {
b.stateFieldLeaves[field].FieldReference().MinusRef()
}
}
for i := 0; i < fieldCount; i++ {
field := types.FieldIndex(i)
delete(b.stateFieldLeaves, field)
delete(b.dirtyIndices, field)
delete(b.dirtyFields, field)
delete(b.sharedFieldReferences, field)
delete(b.stateFieldLeaves, field)
}
state.StateCount.Sub(1)
})
return dst
}
// HashTreeRoot of the beacon state retrieves the Merkle root of the trie
// representation of the beacon state based on the Ethereum Simple Serialize specification.
func (b *BeaconState) HashTreeRoot(ctx context.Context) ([32]byte, error) {
ctx, span := trace.StartSpan(ctx, "beaconState.HashTreeRoot")
defer span.End()
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return [32]byte{}, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return [32]byte{}, err
}
return bytesutil.ToBytes32(b.merkleLayers[len(b.merkleLayers)-1][0]), nil
}
// Initializes the Merkle layers for the beacon state if they are empty.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
if len(b.merkleLayers) > 0 {
return nil
}
protoState, ok := b.ToProtoUnsafe().(*ethpb.BeaconState)
if !ok {
return errors.New("state is of the wrong type")
}
fieldRoots, err := computeFieldRoots(ctx, protoState)
if err != nil {
return err
}
layers := stateutil.Merkleize(fieldRoots)
b.merkleLayers = layers
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFieldCount)
return nil
}
// Recomputes the Merkle layers for the dirty fields in the state.
// WARNING: Caller must acquire the mutex before using.
func (b *BeaconState) recomputeDirtyFields(ctx context.Context) error {
for field := range b.dirtyFields {
root, err := b.rootSelector(ctx, field)
if err != nil {
return err
}
b.merkleLayers[0][field] = root[:]
b.recomputeRoot(int(field))
delete(b.dirtyFields, field)
}
return nil
}
// FieldReferencesCount returns the reference count held by each field. This
// also includes the field trie held by each field.
func (b *BeaconState) FieldReferencesCount() map[string]uint64 {
refMap := make(map[string]uint64)
b.lock.RLock()
defer b.lock.RUnlock()
for i, f := range b.sharedFieldReferences {
refMap[i.String(b.Version())] = uint64(f.Refs())
}
for i, f := range b.stateFieldLeaves {
numOfRefs := uint64(f.FieldReference().Refs())
f.RLock()
if !f.Empty() {
refMap[i.String(b.Version())+"_trie"] = numOfRefs
}
f.RUnlock()
}
return refMap
}
// IsNil checks if the state and the underlying proto
// object are nil.
func (b *BeaconState) IsNil() bool {
return b == nil
}
func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) ([32]byte, error) {
_, span := trace.StartSpan(ctx, "beaconState.rootSelector")
defer span.End()
span.AddAttributes(trace.StringAttribute("field", field.String(b.Version())))
hasher := hash.CustomSHA256Hasher()
switch field {
case genesisTime:
return ssz.Uint64Root(b.genesisTime), nil
case genesisValidatorsRoot:
return b.genesisValidatorsRoot, nil
case slot:
return ssz.Uint64Root(uint64(b.slot)), nil
case eth1DepositIndex:
return ssz.Uint64Root(b.eth1DepositIndex), nil
case fork:
return ssz.ForkRoot(b.fork)
case latestBlockHeader:
return stateutil.BlockHeaderRoot(b.latestBlockHeader)
case blockRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.blockRoots, fieldparams.BlockRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(blockRoots, b.blockRoots)
case stateRoots:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.stateRoots, fieldparams.StateRootsLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(stateRoots, b.stateRoots)
case historicalRoots:
hRoots := make([][]byte, len(b.historicalRoots))
for i := range hRoots {
hRoots[i] = b.historicalRoots[i][:]
}
return ssz.ByteArrayRootWithLimit(hRoots, fieldparams.HistoricalRootsLength)
case eth1Data:
return stateutil.Eth1Root(hasher, b.eth1Data)
case eth1DataVotes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.eth1DataVotes,
fieldparams.Eth1DataVotesLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.eth1DataVotes)
case validators:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.validators, fieldparams.ValidatorRegistryLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, validators)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(validators, b.validators)
case balances:
if features.Get().EnableBalanceTrieComputation {
if b.rebuildTrie[field] {
maxBalCap := uint64(fieldparams.ValidatorRegistryLimit)
elemSize := uint64(8)
balLimit := (maxBalCap*elemSize + 31) / 32
err := b.resetFieldTrie(field, b.balances, balLimit)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(balances, b.balances)
}
return stateutil.Uint64ListRootWithRegistryLimit(b.balances)
case randaoMixes:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(field, b.randaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(randaoMixes, b.randaoMixes)
case slashings:
return ssz.SlashingsRoot(b.slashings)
case previousEpochAttestations:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.previousEpochAttestations,
fieldparams.PreviousEpochAttestationsLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.previousEpochAttestations)
case currentEpochAttestations:
if b.rebuildTrie[field] {
err := b.resetFieldTrie(
field,
b.currentEpochAttestations,
fieldparams.CurrentEpochAttestationsLength,
)
if err != nil {
return [32]byte{}, err
}
delete(b.rebuildTrie, field)
return b.stateFieldLeaves[field].TrieRoot()
}
return b.recomputeFieldTrie(field, b.currentEpochAttestations)
case justificationBits:
return bytesutil.ToBytes32(b.justificationBits), nil
case previousJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.previousJustifiedCheckpoint)
case currentJustifiedCheckpoint:
return ssz.CheckpointRoot(hasher, b.currentJustifiedCheckpoint)
case finalizedCheckpoint:
return ssz.CheckpointRoot(hasher, b.finalizedCheckpoint)
}
return [32]byte{}, errors.New("invalid field index provided")
}
func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interface{}) ([32]byte, error) {
fTrie := b.stateFieldLeaves[index]
// We can't lock the trie directly because the trie's variable gets reassigned,
// and therefore we would call Unlock() on a different object.
fTrieMutex := fTrie.RWMutex
if fTrie.FieldReference().Refs() > 1 {
fTrieMutex.Lock()
fTrie.FieldReference().MinusRef()
newTrie := fTrie.CopyTrie()
b.stateFieldLeaves[index] = newTrie
fTrie = newTrie
fTrieMutex.Unlock()
}
// remove duplicate indexes
b.dirtyIndices[index] = slice.SetUint64(b.dirtyIndices[index])
// sort indexes again
sort.Slice(b.dirtyIndices[index], func(i int, j int) bool {
return b.dirtyIndices[index][i] < b.dirtyIndices[index][j]
})
root, err := fTrie.RecomputeTrie(b.dirtyIndices[index], elements)
if err != nil {
return [32]byte{}, err
}
b.dirtyIndices[index] = []uint64{}
return root, nil
}
func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{}, length uint64) error {
fTrie, err := fieldtrie.NewFieldTrie(index, fieldMap[index], elements, length)
if err != nil {
return err
}
b.stateFieldLeaves[index] = fTrie
b.dirtyIndices[index] = []uint64{}
return nil
}

View File

@@ -1,278 +0,0 @@
package v1_test
import (
"bytes"
"context"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
"github.com/prysmaticlabs/prysm/config/features"
"github.com/prysmaticlabs/prysm/config/params"
"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"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestMain(m *testing.M) {
resetCfg := features.InitWithReset(&features.Flags{EnableBalanceTrieComputation: true})
defer resetCfg()
m.Run()
}
func TestInitializeFromProto(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
type test struct {
name string
state *ethpb.BeaconState
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconState{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconState{},
},
{
name: "full state",
state: pbState,
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := v1.InitializeFromProto(tt.state)
if tt.error != "" {
assert.ErrorContains(t, tt.error, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestInitializeFromProtoUnsafe(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
type test struct {
name string
state *ethpb.BeaconState
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconState{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconState{},
},
{
name: "full state",
state: pbState,
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := v1.InitializeFromProtoUnsafe(tt.state)
if tt.error != "" {
assert.ErrorContains(t, tt.error, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestBeaconState_HashTreeRoot(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
type test struct {
name string
stateModify func(beaconState state.BeaconState) (state.BeaconState, error)
error string
}
initTests := []test{
{
name: "unchanged state",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
return beaconState, nil
},
error: "",
},
{
name: "different slot",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
if err := beaconState.SetSlot(5); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
{
name: "different validator balance",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
val, err := beaconState.ValidatorAtIndex(5)
if err != nil {
return nil, err
}
val.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement
if err := beaconState.UpdateValidatorAtIndex(5, val); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
}
var err error
var oldHTR []byte
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
testState, err = tt.stateModify(testState)
assert.NoError(t, err)
root, err := testState.HashTreeRoot(context.Background())
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
genericHTR, err := pbState.HashTreeRoot()
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
assert.DeepNotEqual(t, []byte{}, root[:], "Received empty hash tree root")
assert.DeepEqual(t, genericHTR[:], root[:], "Expected hash tree root to match generic")
if len(oldHTR) != 0 && bytes.Equal(root[:], oldHTR) {
t.Errorf("Expected HTR to change, received %#x == old %#x", root, oldHTR)
}
oldHTR = root[:]
})
}
}
func TestBeaconState_HashTreeRoot_FieldTrie(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
type test struct {
name string
stateModify func(state.BeaconState) (state.BeaconState, error)
error string
}
initTests := []test{
{
name: "unchanged state",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
return beaconState, nil
},
error: "",
},
{
name: "different slot",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
if err := beaconState.SetSlot(5); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
{
name: "different validator balance",
stateModify: func(beaconState state.BeaconState) (state.BeaconState, error) {
val, err := beaconState.ValidatorAtIndex(5)
if err != nil {
return nil, err
}
val.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement
if err := beaconState.UpdateValidatorAtIndex(5, val); err != nil {
return nil, err
}
return beaconState, nil
},
error: "",
},
}
var err error
var oldHTR []byte
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
testState, err = tt.stateModify(testState)
assert.NoError(t, err)
root, err := testState.HashTreeRoot(context.Background())
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(t, err)
genericHTR, err := pbState.HashTreeRoot()
if err == nil && tt.error != "" {
t.Errorf("Expected error, expected %v, recevied %v", tt.error, err)
}
assert.DeepNotEqual(t, []byte{}, root[:], "Received empty hash tree root")
assert.DeepEqual(t, genericHTR[:], root[:], "Expected hash tree root to match generic")
if len(oldHTR) != 0 && bytes.Equal(root[:], oldHTR) {
t.Errorf("Expected HTR to change, received %#x == old %#x", root, oldHTR)
}
oldHTR = root[:]
})
}
}
func TestBeaconState_AppendValidator_DoesntMutateCopy(t *testing.T) {
st0, err := util.NewBeaconState()
require.NoError(t, err)
st1 := st0.Copy()
originalCount := st1.NumValidators()
val := &ethpb.Validator{Slashed: true}
assert.NoError(t, st0.AppendValidator(val))
assert.Equal(t, originalCount, st1.NumValidators(), "st1 NumValidators mutated")
_, ok := st1.ValidatorIndexByPubkey(bytesutil.ToBytes48(val.PublicKey))
assert.Equal(t, false, ok, "Expected no validator index to be present in st1 for the newly inserted pubkey")
}
func BenchmarkBeaconState(b *testing.B) {
testState, _ := util.DeterministicGenesisState(b, 16000)
pbState, err := v1.ProtobufBeaconState(testState.InnerStateUnsafe())
require.NoError(b, err)
b.Run("Vectorized SHA256", func(b *testing.B) {
st, err := v1.InitializeFromProtoUnsafe(pbState)
require.NoError(b, err)
_, err = st.HashTreeRoot(context.Background())
assert.NoError(b, err)
})
b.Run("Current SHA256", func(b *testing.B) {
_, err := pbState.HashTreeRoot()
require.NoError(b, err)
})
}

View File

@@ -1,54 +0,0 @@
package v1
import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state/types"
"github.com/prysmaticlabs/prysm/config/params"
)
// Ensure type BeaconState below implements BeaconState interface.
var _ state.BeaconState = (*BeaconState)(nil)
func init() {
fieldMap = make(map[types.FieldIndex]types.DataType, params.BeaconConfig().BeaconStateFieldCount)
// Initialize the fixed sized arrays.
fieldMap[types.BlockRoots] = types.BasicArray
fieldMap[types.StateRoots] = types.BasicArray
fieldMap[types.RandaoMixes] = types.BasicArray
// Initialize the composite arrays.
fieldMap[types.Eth1DataVotes] = types.CompositeArray
fieldMap[types.Validators] = types.CompositeArray
fieldMap[types.PreviousEpochAttestations] = types.CompositeArray
fieldMap[types.CurrentEpochAttestations] = types.CompositeArray
fieldMap[types.Balances] = types.CompressedArray
}
// fieldMap keeps track of each field
// to its corresponding data type.
var fieldMap map[types.FieldIndex]types.DataType
// Field Aliases for values from the types package.
const (
genesisTime = types.GenesisTime
genesisValidatorsRoot = types.GenesisValidatorsRoot
slot = types.Slot
fork = types.Fork
latestBlockHeader = types.LatestBlockHeader
blockRoots = types.BlockRoots
stateRoots = types.StateRoots
historicalRoots = types.HistoricalRoots
eth1Data = types.Eth1Data
eth1DataVotes = types.Eth1DataVotes
eth1DepositIndex = types.Eth1DepositIndex
validators = types.Validators
balances = types.Balances
randaoMixes = types.RandaoMixes
slashings = types.Slashings
previousEpochAttestations = types.PreviousEpochAttestations
currentEpochAttestations = types.CurrentEpochAttestations
justificationBits = types.JustificationBits
previousJustifiedCheckpoint = types.PreviousJustifiedCheckpoint
currentJustifiedCheckpoint = types.CurrentJustifiedCheckpoint
finalizedCheckpoint = types.FinalizedCheckpoint
)

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