Compare commits

..

3 Commits

Author SHA1 Message Date
nisdas
54ae2b32b2 Merge branch 'develop' of https://github.com/prysmaticlabs/geth-sharding into e2eProxy 2022-04-12 18:04:39 +08:00
nisdas
b84c1aa3ea hack it in 2022-04-10 16:31:15 +08:00
nisdas
621e149dce add all this stuff 2022-04-09 21:09:06 +08:00
191 changed files with 12448 additions and 2891 deletions

View File

@@ -115,19 +115,18 @@ 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/comparesame:go_default_library",
"//tools/analyzers/maligned:go_default_library",
"//tools/analyzers/cryptorand:go_default_library",
"//tools/analyzers/errcheck:go_default_library",
"//tools/analyzers/featureconfig: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/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/ineffassign: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.9",
go_version = "1.17.6",
nogo = "@//:nogo",
)

View File

@@ -66,7 +66,6 @@ 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 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 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 been finalized, the block will always be part of the canonical chain.
return s.cfg.BeaconDB.IsFinalizedBlock(ctx, 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
}
// ChainHeads returns all possible chain heads (leaves of fork choice tree).

View File

@@ -11,7 +11,6 @@ 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"
@@ -403,13 +402,10 @@ 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, 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))
eth1DepositIndex := int64(finalizedState.Eth1Data().DepositCount - 1)
s.cfg.DepositCache.InsertFinalizedDeposits(ctx, eth1DepositIndex)
// Deposit proofs are only used during state transition and can be safely removed to save space.
if err = s.cfg.DepositCache.PruneProofs(ctx, int64(eth1DepositIndex)); err != nil {
if err = s.cfg.DepositCache.PruneProofs(ctx, eth1DepositIndex); err != nil {
return errors.Wrap(err, "could not prune deposit proofs")
}
return nil

View File

@@ -1516,7 +1516,6 @@ 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++ {
@@ -1530,64 +1529,8 @@ func TestInsertFinalizedDeposits(t *testing.T) {
}
assert.NoError(t, service.insertFinalizedDeposits(ctx, [32]byte{'m', 'o', 'c', 'k'}))
fDeposits := depositCache.FinalizedDeposits(ctx)
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))
assert.Equal(t, 9, int(fDeposits.MerkleTrieIndex), "Finalized deposits not inserted correctly")
deps := depositCache.AllDeposits(ctx, big.NewInt(109))
for _, d := range deps {
assert.DeepEqual(t, [][]byte(nil), d.Proof, "Proofs are not empty")
}

View File

@@ -83,8 +83,7 @@ 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.
blockRoot := bytesutil.ToBytes32(root)
if s.cfg.ForkChoiceStore.HasNode(blockRoot) && s.cfg.ForkChoiceStore.IsCanonical(blockRoot) {
if s.cfg.ForkChoiceStore.IsCanonical(bytesutil.ToBytes32(root)) {
return nil
}

View File

@@ -137,22 +137,6 @@ 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

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(1), cachedDeposits.MerkleTrieIndex)
assert.Equal(t, int64(2), cachedDeposits.MerkleTrieIndex)
var deps [][]byte
for _, d := range oldFinalizedDeposits {
for _, d := range append(oldFinalizedDeposits, &newFinalizedDeposit) {
hash, err := d.Deposit.Data.HashTreeRoot()
require.NoError(t, err, "Could not hash deposit data")
deps = append(deps, hash[:])
@@ -491,140 +491,6 @@ 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)

View File

@@ -120,16 +120,11 @@ 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)
switch {
case errors.Is(err, hexutil.ErrEmptyString):
return false, nil
case err != nil:
if err != nil {
return false, err
case latest == nil:
}
if 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,38 +190,6 @@ 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,12 +10,9 @@ 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"
)
@@ -52,8 +49,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.
@@ -177,78 +174,6 @@ 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) {
@@ -326,15 +251,3 @@ 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,155 +418,6 @@ 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

@@ -26,7 +26,6 @@ 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,32 +2,25 @@ 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
TerminalBlockHash []byte
TerminalBlockHashExists bool
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
}
// NewPayload --
@@ -65,52 +58,3 @@ 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

@@ -77,6 +77,7 @@ 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",
@@ -161,6 +162,7 @@ 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

@@ -3,7 +3,11 @@ 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"
@@ -177,7 +181,79 @@ func (vs *Server) getTerminalBlockHashIfExists(ctx context.Context) ([]byte, boo
return terminalBlockHash.Bytes(), true, nil
}
return vs.ExecutionEngineCaller.GetTerminalBlockHash(ctx)
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
}
}
// activationEpochNotReached returns true if activation epoch has not been reach.
@@ -194,6 +270,18 @@ 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,6 +6,7 @@ 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"
@@ -21,6 +22,26 @@ 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))
@@ -133,6 +154,137 @@ 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,7 +10,6 @@ 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.FieldIdx
field types.FieldIndex
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.FieldIdx, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) {
func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) {
if elements == nil {
return &FieldTrie{
field: field,

View File

@@ -7,7 +7,6 @@ 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"
@@ -19,7 +18,7 @@ import (
)
// ProofFromMerkleLayers creates a proof starting at the leaf index of the state Merkle layers.
func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex int) [][]byte {
func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex types.FieldIndex) [][]byte {
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
proof := make([][]byte, 0)
@@ -50,7 +49,7 @@ func (f *FieldTrie) validateIndices(idxs []uint64) error {
return nil
}
func validateElements(field types.FieldIdx, dataType types.DataType, elements interface{}, length uint64) error {
func validateElements(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) error {
if dataType == types.CompressedArray {
comLength, err := field.ElemsInChunk()
if err != nil {
@@ -66,117 +65,68 @@ func validateElements(field types.FieldIdx, dataType types.DataType, elements in
}
// fieldConverters converts the corresponding field and the provided elements to the appropriate roots.
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())
}
}
func fieldConverters(field types.FieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
switch field {
case types.BlockRoots:
return convertBlockRoots(indices, elements, convertAll)
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")
}
case types.StateRoots:
return convertStateRoots(indices, elements, convertAll)
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")
}
case types.RandaoMixes:
return convertRandaoMixes(indices, elements, convertAll)
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")
}
case types.Eth1DataVotes:
return convertEth1DataVotes(indices, elements, convertAll)
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)
case types.Validators:
return convertValidators(indices, elements, convertAll)
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)
case types.PreviousEpochAttestations, types.CurrentEpochAttestations:
return convertAttestations(indices, elements, convertAll)
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)
case types.Balances:
return convertBalances(indices, elements, convertAll)
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)
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(stateTypes.FieldIndex(5), stateTypes.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
trie, err := fieldtrie.NewFieldTrie(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(stateTypes.FieldIndex(11), stateTypes.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit)
trie, err := fieldtrie.NewFieldTrie(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(stateTypes.FieldIndex(13), stateTypes.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector))
trie, err := fieldtrie.NewFieldTrie(13, stateTypes.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector))
require.NoError(t, err)
newTrie := trie.CopyTrie()

View File

@@ -1,76 +0,0 @@
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,36 +0,0 @@
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

@@ -1,29 +0,0 @@
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

@@ -1,9 +0,0 @@
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

@@ -1,124 +0,0 @@
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

@@ -0,0 +1,134 @@
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

@@ -0,0 +1,51 @@
//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

@@ -0,0 +1,82 @@
//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

@@ -0,0 +1,51 @@
//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

@@ -0,0 +1,82 @@
//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

@@ -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 state_native
package v1

View File

@@ -0,0 +1,14 @@
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

@@ -0,0 +1,493 @@
// 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,4 +1,4 @@
package state_native
package v1
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"

View File

@@ -0,0 +1,46 @@
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,4 +1,4 @@
package state_native
package v1
import (
"fmt"

View File

@@ -0,0 +1,45 @@
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,4 +1,4 @@
package state_native
package v1
import (
"bytes"

View File

@@ -0,0 +1,74 @@
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,4 +1,4 @@
package state_native
package v1
import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"

View File

@@ -1,8 +1,9 @@
package state_native
package v1
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.
@@ -24,8 +25,8 @@ func (b *BeaconState) GenesisValidatorsRoot() []byte {
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (b *BeaconState) Version() int {
return b.version
func (_ *BeaconState) Version() int {
return version.Phase0
}
// Slot of the current beacon chain state.

View File

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

View File

@@ -0,0 +1,39 @@
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

@@ -0,0 +1,250 @@
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

@@ -0,0 +1,18 @@
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,4 +1,4 @@
package state_native
package v1
import (
"context"
@@ -6,7 +6,6 @@ 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"
)
@@ -49,7 +48,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, b.fieldIndexesRev[nativetypes.FinalizedCheckpoint])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
proof = append(proof, branch...)
return proof, nil
}

View File

@@ -0,0 +1,64 @@
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,4 +1,4 @@
package state_native
package v1
import (
"github.com/pkg/errors"

View File

@@ -0,0 +1,74 @@
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

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

View File

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

View File

@@ -1,8 +1,7 @@
package state_native
package v1
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"
)
@@ -12,7 +11,7 @@ func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
defer b.lock.Unlock()
b.justificationBits = val
b.markFieldAsDirty(nativetypes.JustificationBits)
b.markFieldAsDirty(justificationBits)
return nil
}
@@ -22,7 +21,7 @@ func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) erro
defer b.lock.Unlock()
b.previousJustifiedCheckpoint = val
b.markFieldAsDirty(nativetypes.PreviousJustifiedCheckpoint)
b.markFieldAsDirty(previousJustifiedCheckpoint)
return nil
}
@@ -32,7 +31,7 @@ func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error
defer b.lock.Unlock()
b.currentJustifiedCheckpoint = val
b.markFieldAsDirty(nativetypes.CurrentJustifiedCheckpoint)
b.markFieldAsDirty(currentJustifiedCheckpoint)
return nil
}
@@ -42,6 +41,6 @@ func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
defer b.lock.Unlock()
b.finalizedCheckpoint = val
b.markFieldAsDirty(nativetypes.FinalizedCheckpoint)
b.markFieldAsDirty(finalizedCheckpoint)
return nil
}

View File

@@ -1,7 +1,6 @@
package state_native
package v1
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"
)
@@ -12,7 +11,7 @@ func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
defer b.lock.Unlock()
b.eth1Data = val
b.markFieldAsDirty(nativetypes.Eth1Data)
b.markFieldAsDirty(eth1Data)
return nil
}
@@ -22,12 +21,12 @@ func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[nativetypes.Eth1DataVotes].MinusRef()
b.sharedFieldReferences[nativetypes.Eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
b.eth1DataVotes = val
b.markFieldAsDirty(nativetypes.Eth1DataVotes)
b.rebuildTrie[nativetypes.Eth1DataVotes] = true
b.markFieldAsDirty(eth1DataVotes)
b.rebuildTrie[eth1DataVotes] = true
return nil
}
@@ -37,7 +36,7 @@ func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
defer b.lock.Unlock()
b.eth1DepositIndex = val
b.markFieldAsDirty(nativetypes.Eth1DepositIndex)
b.markFieldAsDirty(eth1DepositIndex)
return nil
}
@@ -48,16 +47,16 @@ func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
defer b.lock.Unlock()
votes := b.eth1DataVotes
if b.sharedFieldReferences[nativetypes.Eth1DataVotes].Refs() > 1 {
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[nativetypes.Eth1DataVotes].MinusRef()
b.sharedFieldReferences[nativetypes.Eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[eth1DataVotes].MinusRef()
b.sharedFieldReferences[eth1DataVotes] = stateutil.NewRef(1)
}
b.eth1DataVotes = append(votes, val)
b.markFieldAsDirty(nativetypes.Eth1DataVotes)
b.addDirtyIndices(nativetypes.Eth1DataVotes, []uint64{uint64(len(b.eth1DataVotes) - 1)})
b.markFieldAsDirty(eth1DataVotes)
b.addDirtyIndices(eth1DataVotes, []uint64{uint64(len(b.eth1DataVotes) - 1)})
return nil
}

View File

@@ -1,10 +1,10 @@
package state_native
package v1
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(nativetypes.GenesisTime)
b.markFieldAsDirty(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(nativetypes.GenesisValidatorsRoot)
b.markFieldAsDirty(genesisValidatorsRoot)
return nil
}
@@ -70,7 +70,7 @@ func (b *BeaconState) SetSlot(val types.Slot) error {
defer b.lock.Unlock()
b.slot = val
b.markFieldAsDirty(nativetypes.Slot)
b.markFieldAsDirty(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(nativetypes.Fork)
b.markFieldAsDirty(fork)
return nil
}
@@ -94,15 +94,15 @@ func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[nativetypes.HistoricalRoots].MinusRef()
b.sharedFieldReferences[nativetypes.HistoricalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
roots := make([][32]byte, len(val))
for i, r := range val {
copy(roots[i][:], r)
}
b.historicalRoots = roots
b.markFieldAsDirty(nativetypes.HistoricalRoots)
b.markFieldAsDirty(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[nativetypes.HistoricalRoots].Refs() > 1 {
if b.sharedFieldReferences[historicalRoots].Refs() > 1 {
roots = make([][32]byte, len(b.historicalRoots))
copy(roots, b.historicalRoots)
b.sharedFieldReferences[nativetypes.HistoricalRoots].MinusRef()
b.sharedFieldReferences[nativetypes.HistoricalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[historicalRoots].MinusRef()
b.sharedFieldReferences[historicalRoots] = stateutil.NewRef(1)
}
b.historicalRoots = append(roots, root)
b.markFieldAsDirty(nativetypes.HistoricalRoots)
b.markFieldAsDirty(historicalRoots)
return nil
}
@@ -160,18 +160,18 @@ func (b *BeaconState) recomputeRoot(idx int) {
b.merkleLayers = layers
}
func (b *BeaconState) markFieldAsDirty(field nativetypes.FieldIndex) {
func (b *BeaconState) markFieldAsDirty(field stateTypes.FieldIndex) {
b.dirtyFields[field] = true
}
// addDirtyIndices adds the relevant dirty field indices, so that they
// can be recomputed.
func (b *BeaconState) addDirtyIndices(index nativetypes.FieldIndex, indices []uint64) {
func (b *BeaconState) addDirtyIndices(index stateTypes.FieldIndex, indices []uint64) {
if b.rebuildTrie[index] {
return
}
// Exit early if balance trie computation isn't enabled.
if !features.Get().EnableBalanceTrieComputation && index == nativetypes.Balances {
if !features.Get().EnableBalanceTrieComputation && index == balances {
return
}
totalIndicesLen := len(b.dirtyIndices[index]) + len(indices)

View File

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

View File

@@ -1,9 +1,8 @@
package state_native
package v1
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"
)
@@ -14,8 +13,8 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[nativetypes.StateRoots].MinusRef()
b.sharedFieldReferences[nativetypes.StateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[stateRoots].MinusRef()
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
var rootsArr [fieldparams.StateRootsLength][32]byte
for i := 0; i < len(rootsArr); i++ {
@@ -23,8 +22,8 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error {
}
roots := customtypes.StateRoots(rootsArr)
b.stateRoots = &roots
b.markFieldAsDirty(nativetypes.StateRoots)
b.rebuildTrie[nativetypes.StateRoots] = true
b.markFieldAsDirty(stateRoots)
b.rebuildTrie[stateRoots] = true
return nil
}
@@ -43,19 +42,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[nativetypes.StateRoots]; ref.Refs() > 1 {
if ref := b.sharedFieldReferences[stateRoots]; ref.Refs() > 1 {
// Copy elements in underlying array by reference.
roots := *b.stateRoots
rootsCopy := roots
r = &rootsCopy
ref.MinusRef()
b.sharedFieldReferences[nativetypes.StateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[stateRoots] = stateutil.NewRef(1)
}
r[idx] = stateRoot
b.stateRoots = r
b.markFieldAsDirty(nativetypes.StateRoots)
b.addDirtyIndices(nativetypes.StateRoots, []uint64{idx})
b.markFieldAsDirty(stateRoots)
b.addDirtyIndices(stateRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,201 @@
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

@@ -0,0 +1,208 @@
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

@@ -0,0 +1,479 @@
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

@@ -0,0 +1,278 @@
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

@@ -0,0 +1,54 @@
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
)

View File

@@ -0,0 +1,236 @@
package v1_test
import (
"context"
"reflect"
"strconv"
"testing"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/v1"
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/runtime/interop"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
log "github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
func TestBeaconState_ProtoBeaconStateCompatibility(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.MinimalSpecConfig()
cfg.SlotsPerHistoricalRoot = fieldparams.BlockRootsLength
cfg.EpochsPerHistoricalVector = fieldparams.RandaoMixesLength
params.OverrideBeaconConfig(cfg)
ctx := context.Background()
genesis := setupGenesisState(t, 64)
customState, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
cloned, ok := proto.Clone(genesis).(*ethpb.BeaconState)
assert.Equal(t, true, ok, "Object is not of type *ethpb.BeaconState")
custom := customState.CloneInnerState()
assert.DeepSSZEqual(t, cloned, custom)
r1, err := customState.HashTreeRoot(ctx)
require.NoError(t, err)
beaconState, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
r2, err := beaconState.HashTreeRoot(context.Background())
require.NoError(t, err)
assert.Equal(t, r1, r2, "Mismatched roots")
// We then write to the the state and compare hash tree roots again.
balances := genesis.Balances
balances[0] = 3823
require.NoError(t, customState.SetBalances(balances))
r1, err = customState.HashTreeRoot(ctx)
require.NoError(t, err)
genesis.Balances = balances
beaconState, err = v1.InitializeFromProto(genesis)
require.NoError(t, err)
r2, err = beaconState.HashTreeRoot(context.Background())
require.NoError(t, err)
assert.Equal(t, r1, r2, "Mismatched roots")
}
func setupGenesisState(tb testing.TB, count uint64) *ethpb.BeaconState {
genesisState, _, err := interop.GenerateGenesisState(context.Background(), 0, count)
require.NoError(tb, err, "Could not generate genesis beacon state")
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)))
genesisState.Validators = append(genesisState.Validators, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
genesisState.Balances = append(genesisState.Balances, params.BeaconConfig().MaxEffectiveBalance)
}
return genesisState
}
func BenchmarkCloneValidators_Proto(b *testing.B) {
b.StopTimer()
validators := make([]*ethpb.Validator, 16384)
somePubKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3}
someRoot := [32]byte{3, 4, 5}
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: somePubKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 3,
ExitEpoch: 4,
WithdrawableEpoch: 5,
}
}
b.StartTimer()
for i := 0; i < b.N; i++ {
cloneValidatorsWithProto(validators)
}
}
func BenchmarkCloneValidators_Manual(b *testing.B) {
b.StopTimer()
validators := make([]*ethpb.Validator, 16384)
somePubKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3}
someRoot := [32]byte{3, 4, 5}
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: somePubKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
ActivationEpoch: 3,
ExitEpoch: 4,
WithdrawableEpoch: 5,
}
}
b.StartTimer()
for i := 0; i < b.N; i++ {
cloneValidatorsManually(validators)
}
}
func BenchmarkStateClone_Proto(b *testing.B) {
b.StopTimer()
params.SetupTestConfigCleanup(b)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(b, 64)
b.StartTimer()
for i := 0; i < b.N; i++ {
_, ok := proto.Clone(genesis).(*ethpb.BeaconState)
assert.Equal(b, true, ok, "Entity is not of type *ethpb.BeaconState")
}
}
func BenchmarkStateClone_Manual(b *testing.B) {
b.StopTimer()
params.SetupTestConfigCleanup(b)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(b, 64)
st, err := v1.InitializeFromProto(genesis)
require.NoError(b, err)
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = st.CloneInnerState()
}
}
func cloneValidatorsWithProto(vals []*ethpb.Validator) []*ethpb.Validator {
var ok bool
res := make([]*ethpb.Validator, len(vals))
for i := 0; i < len(res); i++ {
res[i], ok = proto.Clone(vals[i]).(*ethpb.Validator)
if !ok {
log.Debug("Entity is not of type *ethpb.Validator")
}
}
return res
}
func cloneValidatorsManually(vals []*ethpb.Validator) []*ethpb.Validator {
res := make([]*ethpb.Validator, len(vals))
for i := 0; i < len(res); i++ {
val := vals[i]
res[i] = &ethpb.Validator{
PublicKey: val.PublicKey,
WithdrawalCredentials: val.WithdrawalCredentials,
EffectiveBalance: val.EffectiveBalance,
Slashed: val.Slashed,
ActivationEligibilityEpoch: val.ActivationEligibilityEpoch,
ActivationEpoch: val.ActivationEpoch,
ExitEpoch: val.ExitEpoch,
WithdrawableEpoch: val.WithdrawableEpoch,
}
}
return res
}
func TestBeaconState_ImmutabilityWithSharedResources(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(t, 64)
a, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
b := a.Copy()
// Randao mixes
require.DeepEqual(t, a.RandaoMixes(), b.RandaoMixes(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateRandaoMixesAtIndex(1, []byte("foo")))
if reflect.DeepEqual(a.RandaoMixes(), b.RandaoMixes()) {
t.Error("Expect a.RandaoMixes() to be different from b.RandaoMixes()")
}
// Validators
require.DeepEqual(t, a.Validators(), b.Validators(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateValidatorAtIndex(1, &ethpb.Validator{Slashed: true}))
if reflect.DeepEqual(a.Validators(), b.Validators()) {
t.Error("Expect a.Validators() to be different from b.Validators()")
}
// State Roots
require.DeepEqual(t, a.StateRoots(), b.StateRoots(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateStateRootAtIndex(1, bytesutil.ToBytes32([]byte("foo"))))
if reflect.DeepEqual(a.StateRoots(), b.StateRoots()) {
t.Fatal("Expected a.StateRoots() to be different from b.StateRoots()")
}
// Block Roots
require.DeepEqual(t, a.BlockRoots(), b.BlockRoots(), "Test precondition failed, fields are not equal")
require.NoError(t, a.UpdateBlockRootAtIndex(1, bytesutil.ToBytes32([]byte("foo"))))
if reflect.DeepEqual(a.BlockRoots(), b.BlockRoots()) {
t.Fatal("Expected a.BlockRoots() to be different from b.BlockRoots()")
}
}
func TestForkManualCopy_OK(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
genesis := setupGenesisState(t, 64)
a, err := v1.InitializeFromProto(genesis)
require.NoError(t, err)
wantedFork := &ethpb.Fork{
PreviousVersion: []byte{'a', 'b', 'c'},
CurrentVersion: []byte{'d', 'e', 'f'},
Epoch: 0,
}
require.NoError(t, a.SetFork(wantedFork))
pbState, err := v1.ProtobufBeaconState(a.InnerStateUnsafe())
require.NoError(t, err)
require.DeepEqual(t, pbState.Fork, wantedFork)
}

View File

@@ -0,0 +1,36 @@
package v1
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// CurrentEpochParticipation is not supported for phase 0 beacon state.
func (*BeaconState) CurrentEpochParticipation() ([]byte, error) {
return nil, errors.New("CurrentEpochParticipation is not supported for phase 0 beacon state")
}
// PreviousEpochParticipation is not supported for phase 0 beacon state.
func (*BeaconState) PreviousEpochParticipation() ([]byte, error) {
return nil, errors.New("PreviousEpochParticipation is not supported for phase 0 beacon state")
}
// InactivityScores is not supported for phase 0 beacon state.
func (*BeaconState) InactivityScores() ([]uint64, error) {
return nil, errors.New("InactivityScores is not supported for phase 0 beacon state")
}
// CurrentSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) CurrentSyncCommittee() (*ethpb.SyncCommittee, error) {
return nil, errors.New("CurrentSyncCommittee is not supported for phase 0 beacon state")
}
// NextSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) NextSyncCommittee() (*ethpb.SyncCommittee, error) {
return nil, errors.New("NextSyncCommittee is not supported for phase 0 beacon state")
}
// LatestExecutionPayloadHeader is not supported for phase 0 beacon state.
func (*BeaconState) LatestExecutionPayloadHeader() (*ethpb.ExecutionPayloadHeader, error) {
return nil, errors.New("LatestExecutionPayloadHeader is not supported for phase 0 beacon state")
}

View File

@@ -0,0 +1,61 @@
package v1
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// AppendCurrentParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) AppendCurrentParticipationBits(_ byte) error {
return errors.New("AppendCurrentParticipationBits is not supported for phase 0 beacon state")
}
// AppendPreviousParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) AppendPreviousParticipationBits(_ byte) error {
return errors.New("AppendPreviousParticipationBits is not supported for phase 0 beacon state")
}
// AppendInactivityScore is not supported for phase 0 beacon state.
func (*BeaconState) AppendInactivityScore(_ uint64) error {
return errors.New("AppendInactivityScore is not supported for phase 0 beacon state")
}
// SetCurrentSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) SetCurrentSyncCommittee(_ *ethpb.SyncCommittee) error {
return errors.New("SetCurrentSyncCommittee is not supported for phase 0 beacon state")
}
// SetNextSyncCommittee is not supported for phase 0 beacon state.
func (*BeaconState) SetNextSyncCommittee(_ *ethpb.SyncCommittee) error {
return errors.New("SetNextSyncCommittee is not supported for phase 0 beacon state")
}
// SetPreviousParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) SetPreviousParticipationBits(_ []byte) error {
return errors.New("SetPreviousParticipationBits is not supported for phase 0 beacon state")
}
// SetCurrentParticipationBits is not supported for phase 0 beacon state.
func (*BeaconState) SetCurrentParticipationBits(_ []byte) error {
return errors.New("SetCurrentParticipationBits is not supported for phase 0 beacon state")
}
// ModifyPreviousParticipationBits is not supported for phase 0 beacon state.
func (b *BeaconState) ModifyPreviousParticipationBits(mutator func(val []byte) ([]byte, error)) error {
return errors.New("ModifyPreviousParticipationBits is not supported for phase 0 beacon state")
}
// ModifyCurrentParticipationBits is not supported for phase 0 beacon state.
func (b *BeaconState) ModifyCurrentParticipationBits(mutator func(val []byte) ([]byte, error)) error {
return errors.New("ModifyCurrentParticipationBits is not supported for phase 0 beacon state")
}
// SetInactivityScores is not supported for phase 0 beacon state.
func (*BeaconState) SetInactivityScores(_ []uint64) error {
return errors.New("SetInactivityScores is not supported for phase 0 beacon state")
}
// SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(val *ethpb.ExecutionPayloadHeader) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state")
}

View File

@@ -0,0 +1,121 @@
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 = [
"deprecated_getters.go",
"deprecated_setters.go",
"field_roots.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_eth1.go",
"getters_misc.go",
"getters_participation.go",
"getters_randao.go",
"getters_state.go",
"getters_sync_committee.go",
"getters_validator.go",
"proofs.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_eth1.go",
"setters_misc.go",
"setters_participation.go",
"setters_randao.go",
"setters_state.go",
"setters_sync_committee.go",
"setters_validator.go",
"state_trie.go",
"types.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/v2",
visibility = [
"//beacon-chain:__subpackages__",
"//proto/migration:__subpackages__",
"//testing/spectest:__subpackages__",
"//testing/util:__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/v1: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",
],
)
go_test(
name = "go_default_test",
srcs = [
"deprecated_getters_test.go",
"deprecated_setters_test.go",
"getters_block_test.go",
"getters_test.go",
"getters_validator_test.go",
"proofs_test.go",
"setters_test.go",
"state_trie_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",
"//beacon-chain/state/v2:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1: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",
],
)
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

@@ -0,0 +1,54 @@
//go:build !minimal
// +build !minimal
package v2
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 eth2 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"`
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"`
previousJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
currentJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
finalizedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
inactivityScores []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
currentSyncCommittee *ethpb.SyncCommittee `ssz-gen:"true"`
nextSyncCommittee *ethpb.SyncCommittee `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

@@ -0,0 +1,87 @@
//go:build !minimal
// +build !minimal
package v2
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("previousEpochParticipation")
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("currentEpochParticipation")
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("justificationBits")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "1", v)
f, ok = bsType.FieldByName("inactivityScores")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
}

View File

@@ -0,0 +1,54 @@
//go:build minimal
// +build minimal
package v2
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 eth2 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"`
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"`
previousJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
currentJustifiedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
finalizedCheckpoint *ethpb.Checkpoint `ssz-gen:"true"`
inactivityScores []uint64 `ssz-gen:"true" ssz-max:"1099511627776"`
currentSyncCommittee *ethpb.SyncCommittee `ssz-gen:"true"`
nextSyncCommittee *ethpb.SyncCommittee `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

@@ -0,0 +1,87 @@
//go:build minimal
// +build minimal
package v2
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("previousEpochParticipation")
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("currentEpochParticipation")
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("justificationBits")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-size")
assert.Equal(t, "1", v)
f, ok = bsType.FieldByName("inactivityScores")
require.Equal(t, true, ok, "Required field not found")
v = f.Tag.Get("ssz-max")
assert.Equal(t, strconv.Itoa(fieldparams.ValidatorRegistryLimit), v)
}

View File

@@ -0,0 +1,21 @@
package v2
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// PreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) PreviousEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("PreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// CurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) CurrentEpochAttestations() ([]*ethpb.PendingAttestation, error) {
return nil, errors.New("CurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// LatestExecutionPayloadHeader is not supported for hard fork 1 beacon state.
func (*BeaconState) LatestExecutionPayloadHeader() (*ethpb.ExecutionPayloadHeader, error) {
return nil, errors.New("LatestExecutionPayloadHeader is not supported for hard fork 1 beacon state")
}

View File

@@ -0,0 +1,19 @@
package v2
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_CurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.CurrentEpochAttestations()
require.ErrorContains(t, "CurrentEpochAttestations is not supported for hard fork 1 beacon state", err)
}
func TestBeaconState_PreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
_, err := s.PreviousEpochAttestations()
require.ErrorContains(t, "PreviousEpochAttestations is not supported for hard fork 1 beacon state", err)
}

View File

@@ -0,0 +1,36 @@
package v2
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetPreviousEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetPreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// SetCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) SetCurrentEpochAttestations(_ []*ethpb.PendingAttestation) error {
return errors.New("SetCurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// AppendCurrentEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendCurrentEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendCurrentEpochAttestations is not supported for hard fork 1 beacon state")
}
// AppendPreviousEpochAttestations is not supported for HF1 beacon state.
func (*BeaconState) AppendPreviousEpochAttestations(_ *ethpb.PendingAttestation) error {
return errors.New("AppendPreviousEpochAttestations is not supported for hard fork 1 beacon state")
}
// RotateAttestations is not supported for HF1 beacon state.
func (*BeaconState) RotateAttestations() error {
return errors.New("RotateAttestations is not supported for hard fork 1 beacon state")
}
// SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(_ *ethpb.ExecutionPayloadHeader) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state")
}

View File

@@ -0,0 +1,27 @@
package v2
import (
"testing"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestBeaconState_AppendCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendCurrentEpochAttestations is not supported for hard fork 1 beacon state", s.AppendCurrentEpochAttestations(nil))
}
func TestBeaconState_AppendPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "AppendPreviousEpochAttestations is not supported for hard fork 1 beacon state", s.AppendPreviousEpochAttestations(nil))
}
func TestBeaconState_SetCurrentEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetCurrentEpochAttestations is not supported for hard fork 1 beacon state", s.SetCurrentEpochAttestations(nil))
}
func TestBeaconState_SetPreviousEpochAttestations(t *testing.T) {
s := &BeaconState{}
require.ErrorContains(t, "SetPreviousEpochAttestations is not supported for hard fork 1 beacon state", s.SetPreviousEpochAttestations(nil))
}

View File

@@ -0,0 +1,14 @@
package v2
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.BeaconStateAltair) ([][]byte, error) {
return stateutil.ComputeFieldRootsWithHasherAltair(ctx, state)
}

View File

@@ -0,0 +1,505 @@
// Code generated by fastssz. DO NOT EDIT.
// Hash: ec98b14e43fd11e74e0d9e705a7afe74a77706c3e215d7940b11411859873f4b
package v2
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(2736629)
// 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) 'previousEpochParticipation'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.previousEpochParticipation)
// Offset (16) 'currentEpochParticipation'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.currentEpochParticipation)
// 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
}
// Offset (21) 'inactivityScores'
dst = ssz.WriteOffset(dst, offset)
offset += len(b.inactivityScores) * 8
// Field (22) 'currentSyncCommittee'
if b.currentSyncCommittee == nil {
b.currentSyncCommittee = new(ethpb.SyncCommittee)
}
if dst, err = b.currentSyncCommittee.MarshalSSZTo(dst); err != nil {
return
}
// Field (23) 'nextSyncCommittee'
if b.nextSyncCommittee == nil {
b.nextSyncCommittee = new(ethpb.SyncCommittee)
}
if dst, err = b.nextSyncCommittee.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) 'previousEpochParticipation'
if len(b.previousEpochParticipation) > 1099511627776 {
err = ssz.ErrBytesLength
return
}
dst = append(dst, b.previousEpochParticipation...)
// Field (16) 'currentEpochParticipation'
if len(b.currentEpochParticipation) > 1099511627776 {
err = ssz.ErrBytesLength
return
}
dst = append(dst, b.currentEpochParticipation...)
// Field (21) 'inactivityScores'
if len(b.inactivityScores) > 1099511627776 {
err = ssz.ErrListTooBig
return
}
for ii := 0; ii < len(b.inactivityScores); ii++ {
dst = ssz.MarshalUint64(dst, b.inactivityScores[ii])
}
return
}
// UnmarshalSSZ ssz unmarshals the BeaconState object
func (b *BeaconState) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 2736629 {
return ssz.ErrSize
}
tail := buf
var o7, o9, o11, o12, o15, o16, o21 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 < 2736629 {
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) 'previousEpochParticipation'
if o15 = ssz.ReadOffset(buf[2687248:2687252]); o15 > size || o12 > o15 {
return ssz.ErrOffset
}
// Offset (16) 'currentEpochParticipation'
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
}
// Offset (21) 'inactivityScores'
if o21 = ssz.ReadOffset(buf[2687377:2687381]); o21 > size || o16 > o21 {
return ssz.ErrOffset
}
// Field (22) 'currentSyncCommittee'
if b.currentSyncCommittee == nil {
b.currentSyncCommittee = new(ethpb.SyncCommittee)
}
if err = b.currentSyncCommittee.UnmarshalSSZ(buf[2687381:2712005]); err != nil {
return err
}
// Field (23) 'nextSyncCommittee'
if b.nextSyncCommittee == nil {
b.nextSyncCommittee = new(ethpb.SyncCommittee)
}
if err = b.nextSyncCommittee.UnmarshalSSZ(buf[2712005:2736629]); 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) 'previousEpochParticipation'
{
buf = tail[o15:o16]
if len(buf) > 1099511627776 {
return ssz.ErrBytesLength
}
if cap(b.previousEpochParticipation) == 0 {
b.previousEpochParticipation = make([]byte, 0, len(buf))
}
b.previousEpochParticipation = append(b.previousEpochParticipation, buf...)
}
// Field (16) 'currentEpochParticipation'
{
buf = tail[o16:o21]
if len(buf) > 1099511627776 {
return ssz.ErrBytesLength
}
if cap(b.currentEpochParticipation) == 0 {
b.currentEpochParticipation = make([]byte, 0, len(buf))
}
b.currentEpochParticipation = append(b.currentEpochParticipation, buf...)
}
// Field (21) 'inactivityScores'
{
buf = tail[o21:]
num, err := ssz.DivideInt2(len(buf), 8, 1099511627776)
if err != nil {
return err
}
b.inactivityScores = ssz.ExtendUint64(b.inactivityScores, num)
for ii := 0; ii < num; ii++ {
b.inactivityScores[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])
}
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the BeaconState object
func (b *BeaconState) SizeSSZ() (size int) {
size = 2736629
// 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) 'previousEpochParticipation'
size += len(b.previousEpochParticipation)
// Field (16) 'currentEpochParticipation'
size += len(b.currentEpochParticipation)
// Field (21) 'inactivityScores'
size += len(b.inactivityScores) * 8
return
}

View File

@@ -0,0 +1,84 @@
package v2
import (
"fmt"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// LatestBlockHeader stored within the beacon state.
func (b *BeaconState) LatestBlockHeader() *ethpb.BeaconBlockHeader {
if b.latestBlockHeader == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.latestBlockHeaderVal()
}
// latestBlockHeaderVal stored within the beacon state.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestBlockHeaderVal() *ethpb.BeaconBlockHeader {
if b.latestBlockHeader == nil {
return nil
}
hdr := &ethpb.BeaconBlockHeader{
Slot: b.latestBlockHeader.Slot,
ProposerIndex: b.latestBlockHeader.ProposerIndex,
}
parentRoot := make([]byte, len(b.latestBlockHeader.ParentRoot))
bodyRoot := make([]byte, len(b.latestBlockHeader.BodyRoot))
stateRoot := make([]byte, len(b.latestBlockHeader.StateRoot))
copy(parentRoot, b.latestBlockHeader.ParentRoot)
copy(bodyRoot, b.latestBlockHeader.BodyRoot)
copy(stateRoot, b.latestBlockHeader.StateRoot)
hdr.ParentRoot = parentRoot
hdr.BodyRoot = bodyRoot
hdr.StateRoot = stateRoot
return hdr
}
// BlockRoots kept track of in the beacon state.
func (b *BeaconState) BlockRoots() [][]byte {
if b.blockRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockRoots.Slice()
}
// BlockRootAtIndex retrieves a specific block root based on an
// input index value.
func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) {
if b.blockRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
r, err := b.blockRootAtIndex(idx)
if err != nil {
return nil, err
}
return r[:], nil
}
// blockRootAtIndex retrieves a specific block root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) blockRootAtIndex(idx uint64) ([32]byte, error) {
if uint64(len(b.blockRoots)) <= idx {
return [32]byte{}, fmt.Errorf("index %d out of range", idx)
}
return b.blockRoots[idx], nil
}

View File

@@ -0,0 +1,45 @@
package v2
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.BeaconStateAltair{})
},
func(BH *ethpb.BeaconBlockHeader) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{LatestBlockHeader: BH})
},
)
}
func TestBeaconState_BlockRoots(t *testing.T) {
testtmpl.VerifyBeaconStateBlockRootsNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}
func TestBeaconState_BlockRootAtIndex(t *testing.T) {
testtmpl.VerifyBeaconStateBlockRootAtIndexNative(
t,
func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
},
func(BR [][]byte) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{BlockRoots: BR})
},
)
}

View File

@@ -0,0 +1,124 @@
package v2
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()
}
// currentJustifiedCheckpoint 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

@@ -0,0 +1,74 @@
package v2
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.BeaconStateAltair{})
})
}
func TestBeaconState_JustificationBits(t *testing.T) {
testtmpl.VerifyBeaconStateJustificationBits(
t,
func(bits bitfield.Bitvector4) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{JustificationBits: bits})
})
}
func TestBeaconState_PreviousJustifiedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
})
}
func TestBeaconState_PreviousJustifiedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStatePreviousJustifiedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
})
}
func TestBeaconState_CurrentJustifiedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
})
}
func TestBeaconState_CurrentJustifiedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStateCurrentJustifiedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
})
}
func TestBeaconState_FinalizedCheckpointNil(t *testing.T) {
testtmpl.VerifyBeaconStateFinalizedCheckpointNil(
t,
func() (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{})
})
}
func TestBeaconState_FinalizedCheckpoint(t *testing.T) {
testtmpl.VerifyBeaconStateFinalizedCheckpoint(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProtoUnsafe(&ethpb.BeaconStateAltair{FinalizedCheckpoint: cp})
})
}

View File

@@ -0,0 +1,64 @@
package v2
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

@@ -0,0 +1,90 @@
package v2
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.
func (b *BeaconState) GenesisTime() uint64 {
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisTime
}
// GenesisValidatorsRoot of the beacon state.
func (b *BeaconState) GenesisValidatorsRoot() []byte {
b.lock.RLock()
defer b.lock.RUnlock()
return b.genesisValidatorsRoot[:]
}
// Version of the beacon state. This method
// is strictly meant to be used without a lock
// internally.
func (_ *BeaconState) Version() int {
return version.Altair
}
// Slot of the current beacon chain state.
func (b *BeaconState) Slot() types.Slot {
b.lock.RLock()
defer b.lock.RUnlock()
return b.slot
}
// Fork version of the beacon chain.
func (b *BeaconState) Fork() *ethpb.Fork {
if b.fork == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.forkVal()
}
// forkVal version of the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) forkVal() *ethpb.Fork {
if b.fork == nil {
return nil
}
prevVersion := make([]byte, len(b.fork.PreviousVersion))
copy(prevVersion, b.fork.PreviousVersion)
currVersion := make([]byte, len(b.fork.CurrentVersion))
copy(currVersion, b.fork.CurrentVersion)
return &ethpb.Fork{
PreviousVersion: prevVersion,
CurrentVersion: currVersion,
Epoch: b.fork.Epoch,
}
}
// HistoricalRoots based on epochs stored in the beacon state.
func (b *BeaconState) HistoricalRoots() [][]byte {
if b.historicalRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.historicalRoots.Slice()
}
// balancesLength returns the length of the balances slice.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) balancesLength() int {
if b.balances == nil {
return 0
}
return len(b.balances)
}

View File

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

View File

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

View File

@@ -0,0 +1,145 @@
package v2
import (
"fmt"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// ToProtoUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) ToProtoUnsafe() interface{} {
if b == nil {
return nil
}
gvrCopy := b.genesisValidatorsRoot
return &ethpb.BeaconStateAltair{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.fork,
LatestBlockHeader: b.latestBlockHeader,
BlockRoots: b.blockRoots.Slice(),
StateRoots: b.stateRoots.Slice(),
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1Data,
Eth1DataVotes: b.eth1DataVotes,
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validators,
Balances: b.balances,
RandaoMixes: b.randaoMixes.Slice(),
Slashings: b.slashings,
PreviousEpochParticipation: b.previousEpochParticipation,
CurrentEpochParticipation: b.currentEpochParticipation,
JustificationBits: b.justificationBits,
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
FinalizedCheckpoint: b.finalizedCheckpoint,
InactivityScores: b.inactivityScores,
CurrentSyncCommittee: b.currentSyncCommittee,
NextSyncCommittee: b.nextSyncCommittee,
}
}
// ToProto the beacon state into a protobuf for usage.
func (b *BeaconState) ToProto() interface{} {
if b == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
gvrCopy := b.genesisValidatorsRoot
return &ethpb.BeaconStateAltair{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.forkVal(),
LatestBlockHeader: b.latestBlockHeaderVal(),
BlockRoots: b.blockRoots.Slice(),
StateRoots: b.stateRoots.Slice(),
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1DataVal(),
Eth1DataVotes: b.eth1DataVotesVal(),
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validatorsVal(),
Balances: b.balancesVal(),
RandaoMixes: b.randaoMixes.Slice(),
Slashings: b.slashingsVal(),
PreviousEpochParticipation: b.previousEpochParticipationVal(),
CurrentEpochParticipation: b.currentEpochParticipationVal(),
JustificationBits: b.justificationBitsVal(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
FinalizedCheckpoint: b.finalizedCheckpointVal(),
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
NextSyncCommittee: b.nextSyncCommitteeVal(),
}
}
// StateRoots kept track of in the beacon state.
func (b *BeaconState) StateRoots() [][]byte {
if b.stateRoots == nil {
return nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.stateRoots.Slice()
}
// StateRootAtIndex retrieves a specific state root based on an
// input index value.
func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) {
if b.stateRoots == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
r, err := b.stateRootAtIndex(idx)
if err != nil {
return nil, err
}
return r[:], nil
}
// stateRootAtIndex retrieves a specific state root based on an
// input index value.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) stateRootAtIndex(idx uint64) ([32]byte, error) {
if uint64(len(b.stateRoots)) <= idx {
return [32]byte{}, fmt.Errorf("index %d out of range", idx)
}
return b.stateRoots[idx], nil
}
// ProtobufBeaconState transforms an input into beacon state hard fork 1 in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconState(s interface{}) (*ethpb.BeaconStateAltair, error) {
pbState, ok := s.(*ethpb.BeaconStateAltair)
if !ok {
return nil, errors.New("input is not type pb.BeaconStateAltair")
}
return pbState, nil
}
// InnerStateUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) InnerStateUnsafe() interface{} {
return b.ToProtoUnsafe()
}
// CloneInnerState the beacon state into a protobuf for usage.
func (b *BeaconState) CloneInnerState() interface{} {
return b.ToProto()
}

View File

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

View File

@@ -0,0 +1,39 @@
package v2
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.BeaconStateAltair{Slot: 1})
})
}
func TestBeaconState_MatchCurrentJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconStateMatchCurrentJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{CurrentJustifiedCheckpoint: cp})
},
)
}
func TestBeaconState_MatchPreviousJustifiedCheckpt(t *testing.T) {
testtmpl.VerifyBeaconStateMatchPreviousJustifiedCheckptNative(
t,
func(cp *ethpb.Checkpoint) (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{PreviousJustifiedCheckpoint: cp})
},
)
}
func TestBeaconState_ValidatorByPubkey(t *testing.T) {
testtmpl.VerifyBeaconStateValidatorByPubkey(t, func() (state.BeaconState, error) {
return InitializeFromProto(&ethpb.BeaconStateAltair{})
})
}

View File

@@ -0,0 +1,281 @@
package v2
import (
"fmt"
"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"
)
// 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
}
var (
// ErrNilValidatorsInState returns when accessing validators in the state while the state has a
// nil slice for the validators field.
ErrNilValidatorsInState = errors.New("state has nil validator slice")
)
// 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, ErrNilValidatorsInState
}
if uint64(len(b.validators)) <= uint64(idx) {
e := NewValidatorIndexOutOfRangeError(idx)
return nil, &e
}
b.lock.RLock()
defer b.lock.RUnlock()
return v1.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 := v1.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()
}
// balancesVal 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()
}
// slashingsVal 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
}
// inactivityScoresVal of validators participating in consensus on the beacon chain.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) inactivityScoresVal() []uint64 {
if b.inactivityScores == nil {
return nil
}
res := make([]uint64, len(b.inactivityScores))
copy(res, b.inactivityScores)
return res
}
// InactivityScores of validators participating in consensus on the beacon chain.
func (b *BeaconState) InactivityScores() ([]uint64, error) {
if b.inactivityScores == nil {
return nil, nil
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.inactivityScoresVal(), nil
}

View File

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

View File

@@ -0,0 +1,83 @@
package v2
import (
"context"
"encoding/binary"
"github.com/prysmaticlabs/prysm/beacon-chain/state/fieldtrie"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
const (
finalizedRootIndex = uint64(105) // Precomputed value.
)
// FinalizedRootGeneralizedIndex for the beacon state.
func FinalizedRootGeneralizedIndex() uint64 {
return finalizedRootIndex
}
// CurrentSyncCommitteeGeneralizedIndex for the beacon state.
func CurrentSyncCommitteeGeneralizedIndex() uint64 {
return uint64(currentSyncCommittee)
}
// NextSyncCommitteeGeneralizedIndex for the beacon state.
func NextSyncCommitteeGeneralizedIndex() uint64 {
return uint64(nextSyncCommittee)
}
// CurrentSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) CurrentSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
// In case the Merkle layers of the trie are not populated, we need
// to perform some initialization.
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
// Our beacon state uses a "dirty" fields pattern which requires us to
// recompute branches of the Merkle layers that are marked as dirty.
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, currentSyncCommittee), nil
}
// NextSyncCommitteeProof from the state's Merkle trie representation.
func (b *BeaconState) NextSyncCommitteeProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, nextSyncCommittee), nil
}
// FinalizedRootProof crafts a Merkle proof for the finalized root
// contained within the finalized checkpoint of a beacon state.
func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
cpt := b.finalizedCheckpoint
// The epoch field of a finalized checkpoint is the neighbor
// index of the finalized root field in its Merkle tree representation
// of the checkpoint. This neighbor is the first element added to the proof.
epochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(epochBuf, uint64(cpt.Epoch))
epochRoot := bytesutil.ToBytes32(epochBuf)
proof := make([][]byte, 0)
proof = append(proof, epochRoot[:])
branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, finalizedCheckpoint)
proof = append(proof, branch...)
return proof, nil
}

View File

@@ -0,0 +1,105 @@
package v2_test
import (
"context"
"testing"
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
"github.com/prysmaticlabs/prysm/container/trie"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBeaconStateMerkleProofs(t *testing.T) {
ctx := context.Background()
st, _ := util.DeterministicGenesisStateAltair(t, 256)
htr, err := st.HashTreeRoot(ctx)
require.NoError(t, err)
t.Run("current sync committee", func(t *testing.T) {
sc, err := st.CurrentSyncCommittee()
require.NoError(t, err)
// Verify the Merkle proof.
scRoot, err := sc.HashTreeRoot()
require.NoError(t, err)
proof, err := st.CurrentSyncCommitteeProof(ctx)
require.NoError(t, err)
valid := trie.VerifyMerkleProof(htr[:], scRoot[:], v2.CurrentSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
})
t.Run("next sync committee", func(t *testing.T) {
nextSC, err := st.NextSyncCommittee()
require.NoError(t, err)
proof, err := st.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
// Verify the Merkle proof.
nextSCRoot, err := nextSC.HashTreeRoot()
require.NoError(t, err)
valid := trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
// Edit the sync committee.
privKey, err := bls.RandKey()
require.NoError(t, err)
nextSC.AggregatePubkey = privKey.PublicKey().Marshal()
require.NoError(t, st.SetNextSyncCommittee(nextSC))
// Verifying the old Merkle proof for the new value should fail.
nextSCRoot, err = nextSC.HashTreeRoot()
require.NoError(t, err)
valid = trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, false, valid)
// Generating a new, valid proof should pass.
proof, err = st.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
htr, err = st.HashTreeRoot(ctx)
require.NoError(t, err)
valid = trie.VerifyMerkleProof(htr[:], nextSCRoot[:], v2.NextSyncCommitteeGeneralizedIndex(), proof)
require.Equal(t, true, valid)
})
t.Run("finalized root", func(t *testing.T) {
finalizedRoot := st.FinalizedCheckpoint().Root
// Verify the Merkle proof.
htr, err = st.HashTreeRoot(ctx)
require.NoError(t, err)
proof, err := st.FinalizedRootProof(ctx)
require.NoError(t, err)
gIndex := v2.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 := v2.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

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

View File

@@ -0,0 +1,67 @@
package v2
import (
"fmt"
customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// SetLatestBlockHeader in the beacon state.
func (b *BeaconState) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
b.lock.Lock()
defer b.lock.Unlock()
b.latestBlockHeader = ethpb.CopyBeaconBlockHeader(val)
b.markFieldAsDirty(latestBlockHeader)
return nil
}
// SetBlockRoots for the beacon state. Updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBlockRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[blockRoots].MinusRef()
b.sharedFieldReferences[blockRoots] = stateutil.NewRef(1)
var rootsArr [fieldparams.BlockRootsLength][32]byte
for i := 0; i < len(rootsArr); i++ {
copy(rootsArr[i][:], val[i])
}
roots := customtypes.BlockRoots(rootsArr)
b.blockRoots = &roots
b.markFieldAsDirty(blockRoots)
b.rebuildTrie[blockRoots] = true
return nil
}
// UpdateBlockRootAtIndex for the beacon state. Updates the block root
// at a specific index to a new value.
func (b *BeaconState) UpdateBlockRootAtIndex(idx uint64, blockRoot [32]byte) error {
if uint64(len(b.blockRoots)) <= idx {
return fmt.Errorf("invalid index provided %d", idx)
}
b.lock.Lock()
defer b.lock.Unlock()
r := b.blockRoots
if ref := b.sharedFieldReferences[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)
}
r[idx] = blockRoot
b.blockRoots = r
b.markFieldAsDirty(blockRoots)
b.addDirtyIndices(blockRoots, []uint64{idx})
return nil
}

View File

@@ -0,0 +1,46 @@
package v2
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

@@ -0,0 +1,62 @@
package v2
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
}

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