Create New Beacon State Data Structure (#4602)

* begin state service

* begin on the state trie idea

* created beacon state structure

* add in the full clone getter

* return by value instead

* add all setters

* new state setters are being completed

* arrays roots exposed

*  close to finishing all these headerssss

* functionality complete

* added in proto benchmark test

* test for compatibility

* add test for compat

* comments fixed

* add clone

* add clone

* remove underlying copies

* make it immutable

* integrate it into chainservice

* revert

* wrap up comments for package

* address all comments and godocs

* address all comments

* clone the pending attestation properly

* properly clone remaining items

* tests pass fixed bug

* prevent nil pointer exceptions

* fixed up some bugs in the clone comparisons

Co-authored-by: Nishant Das <nish1993@hotmail.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Raul Jordan 2020-01-21 22:36:12 -06:00 committed by GitHub
parent bfda29f2ad
commit abe679e90e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1111 additions and 52 deletions

View File

@ -0,0 +1,37 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"getters.go",
"setters.go",
"types.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/stateutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_protolambda_zssz//merkle:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["types_test.go"],
embed = [":go_default_library"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//shared/interop:go_default_library",
"//shared/params:go_default_library",
"//shared/stateutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
)

View File

@ -0,0 +1,343 @@
package state
import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-bitfield"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// Clone the beacon state into a protobuf for usage.
func (b *BeaconState) Clone() *pbp2p.BeaconState {
return &pbp2p.BeaconState{
GenesisTime: b.GenesisTime(),
Slot: b.Slot(),
Fork: b.Fork(),
LatestBlockHeader: b.LatestBlockHeader(),
BlockRoots: b.BlockRoots(),
StateRoots: b.StateRoots(),
HistoricalRoots: b.HistoricalRoots(),
Eth1Data: b.Eth1Data(),
Eth1DataVotes: b.Eth1DataVotes(),
Eth1DepositIndex: b.Eth1DepositIndex(),
Validators: b.Validators(),
Balances: b.Balances(),
RandaoMixes: b.RandaoMixes(),
Slashings: b.Slashings(),
PreviousEpochAttestations: b.PreviousEpochAttestations(),
CurrentEpochAttestations: b.CurrentEpochAttestations(),
JustificationBits: b.JustificationBits(),
PreviousJustifiedCheckpoint: b.PreviousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: b.CurrentJustifiedCheckpoint(),
FinalizedCheckpoint: b.FinalizedCheckpoint(),
}
}
// GenesisTime of the beacon state as a uint64.
func (b *BeaconState) GenesisTime() uint64 {
return b.state.GenesisTime
}
// Slot of the current beacon chain state.
func (b *BeaconState) Slot() uint64 {
return b.state.Slot
}
// Fork version of the beacon chain.
func (b *BeaconState) Fork() *pbp2p.Fork {
if b.state.Fork == nil {
return nil
}
prevVersion := make([]byte, len(b.state.Fork.PreviousVersion))
copy(prevVersion, b.state.Fork.PreviousVersion)
currVersion := make([]byte, len(b.state.Fork.PreviousVersion))
copy(currVersion, b.state.Fork.PreviousVersion)
return &pbp2p.Fork{
PreviousVersion: prevVersion,
CurrentVersion: currVersion,
Epoch: b.state.Fork.Epoch,
}
}
// LatestBlockHeader stored within the beacon state.
func (b *BeaconState) LatestBlockHeader() *ethpb.BeaconBlockHeader {
if b.state.LatestBlockHeader == nil {
return nil
}
hdr := &ethpb.BeaconBlockHeader{
Slot: b.state.LatestBlockHeader.Slot,
}
var parentRoot [32]byte
var bodyRoot [32]byte
var stateRoot [32]byte
copy(parentRoot[:], b.state.LatestBlockHeader.ParentRoot)
copy(bodyRoot[:], b.state.LatestBlockHeader.BodyRoot)
copy(stateRoot[:], b.state.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.state.BlockRoots == nil {
return nil
}
roots := make([][]byte, len(b.state.BlockRoots))
for i, r := range b.state.BlockRoots {
tmpRt := [32]byte{}
copy(tmpRt[:], r)
roots[i] = tmpRt[:]
}
return roots
}
// StateRoots kept track of in the beacon state.
func (b *BeaconState) StateRoots() [][]byte {
if b.state.StateRoots == nil {
return nil
}
roots := make([][]byte, len(b.state.StateRoots))
for i, r := range b.state.StateRoots {
tmpRt := [32]byte{}
copy(tmpRt[:], r)
roots[i] = tmpRt[:]
}
return roots
}
// HistoricalRoots based on epochs stored in the beacon state.
func (b *BeaconState) HistoricalRoots() [][]byte {
if b.state.HistoricalRoots == nil {
return nil
}
roots := make([][]byte, len(b.state.HistoricalRoots))
for i, r := range b.state.HistoricalRoots {
tmpRt := [32]byte{}
copy(tmpRt[:], r)
roots[i] = tmpRt[:]
}
return roots
}
// Eth1Data corresponding to the proof-of-work chain information stored in the beacon state.
func (b *BeaconState) Eth1Data() *ethpb.Eth1Data {
if b.state.Eth1Data == nil {
return nil
}
eth1data := &ethpb.Eth1Data{
DepositCount: b.state.Eth1Data.DepositCount,
}
var depositRoot [32]byte
var blockHash [32]byte
copy(depositRoot[:], b.state.Eth1Data.DepositRoot)
copy(blockHash[:], b.state.Eth1Data.BlockHash)
eth1data.DepositRoot = depositRoot[:]
eth1data.BlockHash = blockHash[:]
return eth1data
}
// Eth1DataVotes corresponds to votes from eth2 on the canonical proof-of-work chain
// data retrieved from eth1.
func (b *BeaconState) Eth1DataVotes() []*ethpb.Eth1Data {
if b.state.Eth1DataVotes == nil {
return nil
}
res := make([]*ethpb.Eth1Data, len(b.state.Eth1DataVotes))
for i := 0; i < len(res); i++ {
res[i] = &ethpb.Eth1Data{
DepositCount: b.state.Eth1Data.DepositCount,
}
var depositRoot [32]byte
var blockHash [32]byte
copy(depositRoot[:], b.state.Eth1DataVotes[i].DepositRoot)
copy(blockHash[:], b.state.Eth1DataVotes[i].BlockHash)
res[i].DepositRoot = depositRoot[:]
res[i].BlockHash = blockHash[:]
}
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 {
return b.state.Eth1DepositIndex
}
// Validators participating in consensus on the beacon chain.
func (b *BeaconState) Validators() []*ethpb.Validator {
if b.state.Validators == nil {
return nil
}
res := make([]*ethpb.Validator, len(b.state.Validators))
for i := 0; i < len(res); i++ {
val := b.state.Validators[i]
var pubKey [48]byte
copy(pubKey[:], val.PublicKey)
var withdrawalCreds [32]byte
copy(withdrawalCreds[:], val.WithdrawalCredentials)
res[i] = &ethpb.Validator{
PublicKey: pubKey[:],
WithdrawalCredentials: withdrawalCreds[:],
EffectiveBalance: val.EffectiveBalance,
Slashed: val.Slashed,
ActivationEligibilityEpoch: val.ActivationEligibilityEpoch,
ActivationEpoch: val.ActivationEpoch,
ExitEpoch: val.ExitEpoch,
WithdrawableEpoch: val.WithdrawableEpoch,
}
}
return res
}
// Balances of validators participating in consensus on the beacon chain.
func (b *BeaconState) Balances() []uint64 {
if b.state.Balances == nil {
return nil
}
res := make([]uint64, len(b.state.Balances))
copy(res, b.state.Balances)
return res
}
// RandaoMixes of block proposers on the beacon chain.
func (b *BeaconState) RandaoMixes() [][]byte {
if b.state.RandaoMixes == nil {
return nil
}
mixes := make([][]byte, len(b.state.RandaoMixes))
for i, r := range b.state.RandaoMixes {
tmpRt := [32]byte{}
copy(tmpRt[:], r)
mixes[i] = tmpRt[:]
}
return mixes
}
// Slashings of validators on the beacon chain.
func (b *BeaconState) Slashings() []uint64 {
if b.state.Slashings == nil {
return nil
}
res := make([]uint64, len(b.state.Slashings))
copy(res, b.state.Slashings)
return res
}
// PreviousEpochAttestations corresponding to blocks on the beacon chain.
func (b *BeaconState) PreviousEpochAttestations() []*pbp2p.PendingAttestation {
if b.state.PreviousEpochAttestations == nil {
return nil
}
res := make([]*pbp2p.PendingAttestation, len(b.state.PreviousEpochAttestations))
for i := 0; i < len(res); i++ {
res[i] = clonePendingAttestation(b.state.PreviousEpochAttestations[i])
}
return res
}
// CurrentEpochAttestations corresponding to blocks on the beacon chain.
func (b *BeaconState) CurrentEpochAttestations() []*pbp2p.PendingAttestation {
if b.state.CurrentEpochAttestations == nil {
return nil
}
res := make([]*pbp2p.PendingAttestation, len(b.state.CurrentEpochAttestations))
for i := 0; i < len(res); i++ {
res[i] = clonePendingAttestation(b.state.CurrentEpochAttestations[i])
}
return res
}
// JustificationBits marking which epochs have been justified in the beacon chain.
func (b *BeaconState) JustificationBits() bitfield.Bitvector4 {
if b.state.JustificationBits == nil {
return nil
}
res := make([]byte, len(b.state.JustificationBits.Bytes()))
copy(res, b.state.JustificationBits.Bytes())
return res
}
// PreviousJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) PreviousJustifiedCheckpoint() *ethpb.Checkpoint {
if b.state.PreviousJustifiedCheckpoint == nil {
return nil
}
cp := &ethpb.Checkpoint{
Epoch: b.state.PreviousJustifiedCheckpoint.Epoch,
}
var root [32]byte
copy(root[:], b.state.PreviousJustifiedCheckpoint.Root)
cp.Root = root[:]
return cp
}
// CurrentJustifiedCheckpoint denoting an epoch and block root.
func (b *BeaconState) CurrentJustifiedCheckpoint() *ethpb.Checkpoint {
if b.state.CurrentJustifiedCheckpoint == nil {
return nil
}
cp := &ethpb.Checkpoint{
Epoch: b.state.CurrentJustifiedCheckpoint.Epoch,
}
var root [32]byte
copy(root[:], b.state.CurrentJustifiedCheckpoint.Root)
cp.Root = root[:]
return cp
}
// FinalizedCheckpoint denoting an epoch and block root.
func (b *BeaconState) FinalizedCheckpoint() *ethpb.Checkpoint {
if b.state.FinalizedCheckpoint == nil {
return nil
}
cp := &ethpb.Checkpoint{
Epoch: b.state.FinalizedCheckpoint.Epoch,
}
var root [32]byte
copy(root[:], b.state.FinalizedCheckpoint.Root)
cp.Root = root[:]
return cp
}
func clonePendingAttestation(att *pbp2p.PendingAttestation) *pbp2p.PendingAttestation {
var aggBits bitfield.Bitlist
copy(aggBits, att.AggregationBits)
var attData *ethpb.AttestationData
if att.Data != nil {
var beaconRoot [32]byte
copy(beaconRoot[:], att.Data.BeaconBlockRoot)
var sourceRoot [32]byte
copy(sourceRoot[:], att.Data.Source.Root)
var targetRoot [32]byte
copy(targetRoot[:], att.Data.Target.Root)
attData = &ethpb.AttestationData{
Slot: att.Data.Slot,
CommitteeIndex: att.Data.CommitteeIndex,
BeaconBlockRoot: beaconRoot[:],
Source: &ethpb.Checkpoint{
Epoch: att.Data.Source.Epoch,
Root: sourceRoot[:],
},
Target: &ethpb.Checkpoint{
Epoch: att.Data.Target.Epoch,
Root: targetRoot[:],
},
}
}
return &pbp2p.PendingAttestation{
AggregationBits: aggBits,
Data: attData,
InclusionDelay: att.InclusionDelay,
ProposerIndex: att.ProposerIndex,
}
}

View File

@ -0,0 +1,353 @@
package state
import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-bitfield"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/stateutil"
)
type fieldIndex int
// 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.
const (
genesisTime fieldIndex = iota
slot
fork
latestBlockHeader
blockRoots
stateRoots
historicalRoots
eth1Data
eth1DataVotes
eth1DepositIndex
validators
balances
randaoMixes
slashings
previousEpochAttestations
currentEpochAttestations
justificationBits
previousJustifiedCheckpoint
currentJustifiedCheckpoint
finalizedCheckpoint
)
// SetGenesisTime for the beacon state.
func (b *BeaconState) SetGenesisTime(val uint64) error {
b.state.GenesisTime = val
root := stateutil.Uint64Root(val)
b.lock.Lock()
b.merkleLayers[0][genesisTime] = root[:]
b.recomputeRoot(int(genesisTime))
b.lock.Unlock()
return nil
}
// SetSlot for the beacon state.
func (b *BeaconState) SetSlot(val uint64) error {
b.state.Slot = val
root := stateutil.Uint64Root(val)
b.lock.Lock()
b.merkleLayers[0][slot] = root[:]
b.recomputeRoot(int(slot))
b.lock.Unlock()
return nil
}
// SetFork version for the beacon chain.
func (b *BeaconState) SetFork(val *pbp2p.Fork) error {
root, err := stateutil.ForkRoot(val)
if err != nil {
return err
}
b.state.Fork = val
b.lock.Lock()
b.merkleLayers[0][fork] = root[:]
b.recomputeRoot(int(fork))
b.lock.Unlock()
return nil
}
// SetLatestBlockHeader in the beacon state.
func (b *BeaconState) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
root, err := stateutil.BlockHeaderRoot(val)
if err != nil {
return err
}
b.state.LatestBlockHeader = val
b.lock.Lock()
b.merkleLayers[0][latestBlockHeader] = root[:]
b.recomputeRoot(int(latestBlockHeader))
b.lock.Unlock()
return nil
}
// SetBlockRoots for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBlockRoots(val [][]byte) error {
root, err := stateutil.RootsArrayHashTreeRoot(val, params.BeaconConfig().SlotsPerHistoricalRoot, "BlockRoots")
if err != nil {
return err
}
b.state.BlockRoots = val
b.lock.Lock()
b.merkleLayers[0][blockRoots] = root[:]
b.recomputeRoot(int(blockRoots))
b.lock.Unlock()
return nil
}
// SetStateRoots for the beacon state. This PR updates the entire
// to a new value by overwriting the previous one.
func (b *BeaconState) SetStateRoots(val [][]byte) error {
root, err := stateutil.RootsArrayHashTreeRoot(val, params.BeaconConfig().SlotsPerHistoricalRoot, "StateRoots")
if err != nil {
return err
}
b.state.StateRoots = val
b.lock.Lock()
b.merkleLayers[0][stateRoots] = root[:]
b.recomputeRoot(int(stateRoots))
b.lock.Unlock()
return nil
}
// SetHistoricalRoots for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
root, err := stateutil.HistoricalRootsRoot(val)
if err != nil {
return err
}
b.state.HistoricalRoots = val
b.lock.Lock()
b.merkleLayers[0][historicalRoots] = root[:]
b.recomputeRoot(int(historicalRoots))
b.lock.Unlock()
return nil
}
// SetEth1Data for the beacon state.
func (b *BeaconState) SetEth1Data(val *ethpb.Eth1Data) error {
root, err := stateutil.Eth1Root(val)
if err != nil {
return err
}
b.state.Eth1Data = val
b.lock.Lock()
b.merkleLayers[0][eth1Data] = root[:]
b.recomputeRoot(int(eth1Data))
b.lock.Unlock()
return nil
}
// SetEth1DataVotes for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
root, err := stateutil.Eth1DataVotesRoot(val)
if err != nil {
return err
}
b.state.Eth1DataVotes = val
b.lock.Lock()
b.merkleLayers[0][eth1DataVotes] = root[:]
b.recomputeRoot(int(eth1DataVotes))
b.lock.Unlock()
return nil
}
// SetEth1DepositIndex for the beacon state.
func (b *BeaconState) SetEth1DepositIndex(val uint64) error {
b.state.Eth1DepositIndex = val
root := stateutil.Uint64Root(val)
b.lock.Lock()
b.merkleLayers[0][eth1DepositIndex] = root[:]
b.recomputeRoot(int(eth1DepositIndex))
b.lock.Unlock()
return nil
}
// SetValidators for the beacon state. This PR updates the entire
// to a new value by overwriting the previous one.
func (b *BeaconState) SetValidators(val []*ethpb.Validator) error {
root, err := stateutil.ValidatorRegistryRoot(val)
if err != nil {
return err
}
b.state.Validators = val
b.lock.Lock()
b.merkleLayers[0][validators] = root[:]
b.recomputeRoot(int(validators))
b.lock.Unlock()
return nil
}
// SetBalances for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetBalances(val []uint64) error {
root, err := stateutil.ValidatorBalancesRoot(val)
if err != nil {
return err
}
b.state.Balances = val
b.lock.Lock()
b.merkleLayers[0][balances] = root[:]
b.recomputeRoot(int(balances))
b.lock.Unlock()
return nil
}
// SetRandaoMixes for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetRandaoMixes(val [][]byte) error {
root, err := stateutil.RootsArrayHashTreeRoot(val, params.BeaconConfig().EpochsPerHistoricalVector, "RandaoMixes")
if err != nil {
return err
}
b.state.RandaoMixes = val
b.lock.Lock()
b.merkleLayers[0][randaoMixes] = root[:]
b.recomputeRoot(int(randaoMixes))
b.lock.Unlock()
return nil
}
// SetSlashings for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetSlashings(val []uint64) error {
root, err := stateutil.SlashingsRoot(val)
if err != nil {
return err
}
b.state.Slashings = val
b.lock.Lock()
b.merkleLayers[0][slashings] = root[:]
b.recomputeRoot(int(slashings))
b.lock.Unlock()
return nil
}
// SetPreviousEpochAttestations for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetPreviousEpochAttestations(val []*pbp2p.PendingAttestation) error {
root, err := stateutil.EpochAttestationsRoot(val)
if err != nil {
return err
}
b.state.PreviousEpochAttestations = val
b.lock.Lock()
b.merkleLayers[0][previousEpochAttestations] = root[:]
b.recomputeRoot(int(previousEpochAttestations))
b.lock.Unlock()
return nil
}
// SetCurrentEpochAttestations for the beacon state. This PR updates the entire
// list to a new value by overwriting the previous one.
func (b *BeaconState) SetCurrentEpochAttestations(val []*pbp2p.PendingAttestation) error {
root, err := stateutil.EpochAttestationsRoot(val)
if err != nil {
return err
}
b.state.CurrentEpochAttestations = val
b.lock.Lock()
b.merkleLayers[0][currentEpochAttestations] = root[:]
b.recomputeRoot(int(currentEpochAttestations))
b.lock.Unlock()
return nil
}
// SetJustificationBits for the beacon state.
func (b *BeaconState) SetJustificationBits(val bitfield.Bitvector4) error {
root := bytesutil.ToBytes32(b.state.JustificationBits)
b.state.JustificationBits = val
b.lock.Lock()
b.merkleLayers[0][justificationBits] = root[:]
b.recomputeRoot(int(justificationBits))
b.lock.Unlock()
return nil
}
// SetPreviousJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetPreviousJustifiedCheckpoint(val *ethpb.Checkpoint) error {
root, err := stateutil.CheckpointRoot(val)
if err != nil {
return err
}
b.state.PreviousJustifiedCheckpoint = val
b.lock.Lock()
b.merkleLayers[0][previousJustifiedCheckpoint] = root[:]
b.recomputeRoot(int(previousJustifiedCheckpoint))
b.lock.Unlock()
return nil
}
// SetCurrentJustifiedCheckpoint for the beacon state.
func (b *BeaconState) SetCurrentJustifiedCheckpoint(val *ethpb.Checkpoint) error {
root, err := stateutil.CheckpointRoot(val)
if err != nil {
return err
}
b.state.CurrentJustifiedCheckpoint = val
b.lock.Lock()
b.merkleLayers[0][currentJustifiedCheckpoint] = root[:]
b.recomputeRoot(int(currentJustifiedCheckpoint))
b.lock.Unlock()
return nil
}
// SetFinalizedCheckpoint for the beacon state.
func (b *BeaconState) SetFinalizedCheckpoint(val *ethpb.Checkpoint) error {
root, err := stateutil.CheckpointRoot(val)
if err != nil {
return err
}
b.state.FinalizedCheckpoint = val
b.lock.Lock()
b.merkleLayers[0][finalizedCheckpoint] = root[:]
b.recomputeRoot(int(finalizedCheckpoint))
b.lock.Unlock()
return nil
}
// Recomputes the branch up the index in the Merkle trie representation
// of the beacon state. This method performs map reads and the caller MUST
// hold the lock before calling this method.
func (b *BeaconState) recomputeRoot(idx int) {
layers := b.merkleLayers
// The merkle tree structure looks as follows:
// [[r1, r2, r3, r4], [parent1, parent2], [root]]
// Using information about the index which changed, idx, we recompute
// only its branch up the tree.
currentIndex := idx
root := b.merkleLayers[0][idx]
for i := 0; i < len(layers)-1; i++ {
isLeft := currentIndex%2 == 0
neighborIdx := currentIndex ^ 1
neighbor := make([]byte, 32)
if layers[i] != nil && len(layers[i]) != 0 && neighborIdx < len(layers[i]) {
neighbor = layers[i][neighborIdx]
}
if isLeft {
parentHash := hashutil.Hash(append(root, neighbor...))
root = parentHash[:]
} else {
parentHash := hashutil.Hash(append(neighbor, root...))
root = parentHash[:]
}
parentIdx := currentIndex / 2
// Update the cached layers at the parent index.
layers[i+1][parentIdx] = root
currentIndex = parentIdx
}
b.merkleLayers = layers
}

View File

@ -0,0 +1,71 @@
package state
import (
"sync"
"github.com/gogo/protobuf/proto"
"github.com/protolambda/zssz/merkle"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/stateutil"
)
// 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 {
state *pbp2p.BeaconState
lock sync.RWMutex
merkleLayers [][][]byte
}
// InitializeFromProto the beacon state from a protobuf representation.
func InitializeFromProto(st *pbp2p.BeaconState) (*BeaconState, error) {
fieldRoots, err := stateutil.ComputeFieldRoots(st)
if err != nil {
return nil, err
}
layers := merkleize(fieldRoots)
return &BeaconState{
state: proto.Clone(st).(*pbp2p.BeaconState),
merkleLayers: layers,
}, nil
}
// HashTreeRoot of the beacon state retrieves the Merkle root of the trie
// representation of the beacon state based on the eth2 Simple Serialize specification.
func (b *BeaconState) HashTreeRoot() [32]byte {
b.lock.RLock()
defer b.lock.RUnlock()
return bytesutil.ToBytes32(b.merkleLayers[len(b.merkleLayers)-1][0])
}
// Merkleize 32-byte leaves into a Merkle trie for its adequate depth, returning
// the resulting layers of the trie based on the appropriate depth. This function
// pads the leaves to a power-of-two length.
func merkleize(leaves [][]byte) [][][]byte {
layers := make([][][]byte, merkle.GetDepth(uint64(len(leaves)))+1)
for len(leaves) != 32 {
leaves = append(leaves, make([]byte, 32))
}
currentLayer := leaves
layers[0] = currentLayer
// We keep track of the hash layers of a Merkle trie until we reach
// the top layer of length 1, which contains the single root element.
// [Root] -> Top layer has length 1.
// [E] [F] -> This layer has length 2.
// [A] [B] [C] [D] -> The bottom layer has length 4 (needs to be a power of two).
i := 1
for len(currentLayer) > 1 && i < len(layers) {
layer := make([][]byte, 0)
for i := 0; i < len(currentLayer); i += 2 {
hashedChunk := hashutil.Hash(append(currentLayer[i], currentLayer[i+1]...))
layer = append(layer, hashedChunk[:])
}
currentLayer = layer
layers[i] = currentLayer
i++
}
return layers
}

View File

@ -0,0 +1,173 @@
package state
import (
"strconv"
"testing"
"github.com/gogo/protobuf/proto"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/interop"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/stateutil"
)
func TestBeaconState_ProtoBeaconStateCompatibility(t *testing.T) {
params.UseMinimalConfig()
genesis := setupGenesisState(t, 64)
customState, err := InitializeFromProto(genesis)
if err != nil {
t.Fatal(err)
}
cloned := proto.Clone(genesis).(*pb.BeaconState)
custom := customState.Clone()
if !proto.Equal(cloned, custom) {
t.Fatal("Cloned states did not match")
}
r1 := customState.HashTreeRoot()
r2, err := stateutil.HashTreeRootState(genesis)
if err != nil {
t.Fatal(err)
}
if r1 != r2 {
t.Fatalf("Mismatched roots, custom HTR %#x != regular HTR %#x", r1, r2)
}
// We then write to the the state and compare hash tree roots again.
balances := genesis.Balances
balances[0] = 3823
if err := customState.SetBalances(balances); err != nil {
t.Fatal(err)
}
r1 = customState.HashTreeRoot()
genesis.Balances = balances
r2, err = stateutil.HashTreeRootState(genesis)
if err != nil {
t.Fatal(err)
}
if r1 != r2 {
t.Fatalf("Mismatched roots, custom HTR %#x != regular HTR %#x", r1, r2)
}
}
func setupGenesisState(tb testing.TB, count uint64) *pb.BeaconState {
genesisState, _, err := interop.GenerateGenesisState(0, count)
if err != nil {
tb.Fatalf("Could not generate genesis beacon state: %v", err)
}
for i := uint64(1); i < count; i++ {
someRoot := [32]byte{}
someKey := [48]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 := [48]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 := [48]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.UseMinimalConfig()
genesis := setupGenesisState(b, 64)
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = proto.Clone(genesis).(*pb.BeaconState)
}
}
func BenchmarkStateClone_Manual(b *testing.B) {
b.StopTimer()
params.UseMinimalConfig()
genesis := setupGenesisState(b, 64)
st, err := InitializeFromProto(genesis)
if err != nil {
b.Fatal(err)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = st.Clone()
}
}
func cloneValidatorsWithProto(vals []*ethpb.Validator) []*ethpb.Validator {
res := make([]*ethpb.Validator, len(vals))
for i := 0; i < len(res); i++ {
res[i] = proto.Clone(vals[i]).(*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
}

View File

@ -7,6 +7,7 @@ import (
"github.com/protolambda/zssz/merkle"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
)
@ -16,6 +17,15 @@ var (
lock sync.RWMutex
)
// RootsArrayHashTreeRoot computes the Merkle root of arrays of 32-byte hashes, such as [64][32]byte
// according to the Simple Serialize specification of eth2.
func RootsArrayHashTreeRoot(vals [][]byte, length uint64, fieldName string) ([32]byte, error) {
if featureconfig.Get().EnableSSZCache {
return cachedHasher.arraysRoot(vals, length, fieldName)
}
return nocachedHasher.arraysRoot(vals, length, fieldName)
}
func (h *stateRootHasher) arraysRoot(input [][]byte, length uint64, fieldName string) ([32]byte, error) {
lock.Lock()
if _, ok := layersCache[fieldName]; !ok && h.rootsCache != nil {

View File

@ -8,9 +8,20 @@ import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
)
// EpochAttestationsRoot computes the HashTreeRoot Merkleization of
// a list of pending attestation values according to the eth2
// Simple Serialize specification.
func EpochAttestationsRoot(atts []*pb.PendingAttestation) ([32]byte, error) {
if featureconfig.Get().EnableSSZCache {
return cachedHasher.epochAttestationsRoot(atts)
}
return nocachedHasher.epochAttestationsRoot(atts)
}
func marshalAttestationData(data *ethpb.AttestationData) []byte {
enc := make([]byte, 128)
@ -67,14 +78,14 @@ func attestationDataRoot(data *ethpb.AttestationData) ([32]byte, error) {
fieldRoots[2] = data.BeaconBlockRoot
// Source
sourceRoot, err := checkpointRoot(data.Source)
sourceRoot, err := CheckpointRoot(data.Source)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute source checkpoint merkleization")
}
fieldRoots[3] = sourceRoot[:]
// Target
targetRoot, err := checkpointRoot(data.Target)
targetRoot, err := CheckpointRoot(data.Target)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute target checkpoint merkleization")
}

View File

@ -10,7 +10,10 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)
func blockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) {
// BlockHeaderRoot computes the HashTreeRoot Merkleization of
// a BeaconBlockHeader struct according to the eth2
// Simple Serialize specification.
func BlockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) {
fieldRoots := make([][]byte, 4)
if header != nil {
headerSlotBuf := make([]byte, 8)
@ -24,7 +27,10 @@ func blockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) {
return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func eth1Root(eth1Data *ethpb.Eth1Data) ([32]byte, error) {
// Eth1Root computes the HashTreeRoot Merkleization of
// a BeaconBlockHeader struct according to the eth2
// Simple Serialize specification.
func Eth1Root(eth1Data *ethpb.Eth1Data) ([32]byte, error) {
fieldRoots := make([][]byte, 3)
for i := 0; i < len(fieldRoots); i++ {
fieldRoots[i] = make([]byte, 32)
@ -44,10 +50,13 @@ func eth1Root(eth1Data *ethpb.Eth1Data) ([32]byte, error) {
return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func eth1DataVotesRoot(eth1DataVotes []*ethpb.Eth1Data) ([32]byte, error) {
// Eth1DataVotesRoot computes the HashTreeRoot Merkleization of
// a list of Eth1Data structs according to the eth2
// Simple Serialize specification.
func Eth1DataVotesRoot(eth1DataVotes []*ethpb.Eth1Data) ([32]byte, error) {
eth1VotesRoots := make([][]byte, 0)
for i := 0; i < len(eth1DataVotes); i++ {
eth1, err := eth1Root(eth1DataVotes[i])
eth1, err := Eth1Root(eth1DataVotes[i])
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute eth1data merkleization")
}

View File

@ -47,71 +47,92 @@ func HashTreeRootState(state *pb.BeaconState) ([32]byte, error) {
return nocachedHasher.hashTreeRootState(state)
}
// ComputeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func ComputeFieldRoots(state *pb.BeaconState) ([][]byte, error) {
if featureconfig.Get().EnableSSZCache {
return cachedHasher.computeFieldRoots(state)
}
return nocachedHasher.computeFieldRoots(state)
}
func (h *stateRootHasher) hashTreeRootState(state *pb.BeaconState) ([32]byte, error) {
var fieldRoots [][]byte
var err error
if featureconfig.Get().EnableSSZCache {
fieldRoots, err = cachedHasher.computeFieldRoots(state)
if err != nil {
return [32]byte{}, err
}
}
fieldRoots, err = nocachedHasher.computeFieldRoots(state)
if err != nil {
return [32]byte{}, err
}
return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func (h *stateRootHasher) computeFieldRoots(state *pb.BeaconState) ([][]byte, error) {
if state == nil {
return [32]byte{}, errors.New("nil state")
return nil, errors.New("nil state")
}
// There are 20 fields in the beacon state.
fieldRoots := make([][]byte, 20)
// Genesis time root.
genesisBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(genesisBuf, state.GenesisTime)
genesisBufRoot := bytesutil.ToBytes32(genesisBuf)
fieldRoots[0] = genesisBufRoot[:]
genesisRoot := Uint64Root(state.GenesisTime)
fieldRoots[0] = genesisRoot[:]
// Slot root.
slotBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(slotBuf, state.Slot)
slotBufRoot := bytesutil.ToBytes32(slotBuf)
fieldRoots[1] = slotBufRoot[:]
slotRoot := Uint64Root(state.Slot)
fieldRoots[1] = slotRoot[:]
// Fork data structure root.
forkHashTreeRoot, err := forkRoot(state.Fork)
forkHashTreeRoot, err := ForkRoot(state.Fork)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute fork merkleization")
return nil, errors.Wrap(err, "could not compute fork merkleization")
}
fieldRoots[2] = forkHashTreeRoot[:]
// BeaconBlockHeader data structure root.
headerHashTreeRoot, err := blockHeaderRoot(state.LatestBlockHeader)
headerHashTreeRoot, err := BlockHeaderRoot(state.LatestBlockHeader)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute block header merkleization")
return nil, errors.Wrap(err, "could not compute block header merkleization")
}
fieldRoots[3] = headerHashTreeRoot[:]
// BlockRoots array root.
blockRootsRoot, err := h.arraysRoot(state.BlockRoots, params.BeaconConfig().SlotsPerHistoricalRoot, "BlockRoots")
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute block roots merkleization")
return nil, errors.Wrap(err, "could not compute block roots merkleization")
}
fieldRoots[4] = blockRootsRoot[:]
// StateRoots array root.
stateRootsRoot, err := h.arraysRoot(state.StateRoots, params.BeaconConfig().SlotsPerHistoricalRoot, "StateRoots")
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute state roots merkleization")
return nil, errors.Wrap(err, "could not compute state roots merkleization")
}
fieldRoots[5] = stateRootsRoot[:]
// HistoricalRoots slice root.
historicalRootsRt, err := historicalRootsRoot(state.HistoricalRoots)
historicalRootsRt, err := HistoricalRootsRoot(state.HistoricalRoots)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute historical roots merkleization")
return nil, errors.Wrap(err, "could not compute historical roots merkleization")
}
fieldRoots[6] = historicalRootsRt[:]
// Eth1Data data structure root.
eth1HashTreeRoot, err := eth1Root(state.Eth1Data)
eth1HashTreeRoot, err := Eth1Root(state.Eth1Data)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute eth1data merkleization")
return nil, errors.Wrap(err, "could not compute eth1data merkleization")
}
fieldRoots[7] = eth1HashTreeRoot[:]
// Eth1DataVotes slice root.
eth1VotesRoot, err := eth1DataVotesRoot(state.Eth1DataVotes)
eth1VotesRoot, err := Eth1DataVotesRoot(state.Eth1DataVotes)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute eth1data votes merkleization")
return nil, errors.Wrap(err, "could not compute eth1data votes merkleization")
}
fieldRoots[8] = eth1VotesRoot[:]
@ -124,42 +145,42 @@ func (h *stateRootHasher) hashTreeRootState(state *pb.BeaconState) ([32]byte, er
// Validators slice root.
validatorsRoot, err := h.validatorRegistryRoot(state.Validators)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute validator registry merkleization")
return nil, errors.Wrap(err, "could not compute validator registry merkleization")
}
fieldRoots[10] = validatorsRoot[:]
// Balances slice root.
balancesRoot, err := validatorBalancesRoot(state.Balances)
balancesRoot, err := ValidatorBalancesRoot(state.Balances)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute validator balances merkleization")
return nil, errors.Wrap(err, "could not compute validator balances merkleization")
}
fieldRoots[11] = balancesRoot[:]
// RandaoMixes array root.
randaoRootsRoot, err := h.arraysRoot(state.RandaoMixes, params.BeaconConfig().EpochsPerHistoricalVector, "RandaoMixes")
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute randao roots merkleization")
return nil, errors.Wrap(err, "could not compute randao roots merkleization")
}
fieldRoots[12] = randaoRootsRoot[:]
// Slashings array root.
slashingsRootsRoot, err := slashingsRoot(state.Slashings)
slashingsRootsRoot, err := SlashingsRoot(state.Slashings)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute slashings merkleization")
return nil, errors.Wrap(err, "could not compute slashings merkleization")
}
fieldRoots[13] = slashingsRootsRoot[:]
// PreviousEpochAttestations slice root.
prevAttsRoot, err := h.epochAttestationsRoot(state.PreviousEpochAttestations)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute previous epoch attestations merkleization")
return nil, errors.Wrap(err, "could not compute previous epoch attestations merkleization")
}
fieldRoots[14] = prevAttsRoot[:]
// CurrentEpochAttestations slice root.
currAttsRoot, err := h.epochAttestationsRoot(state.CurrentEpochAttestations)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute previous epoch attestations merkleization")
return nil, errors.Wrap(err, "could not compute previous epoch attestations merkleization")
}
fieldRoots[15] = currAttsRoot[:]
@ -168,34 +189,42 @@ func (h *stateRootHasher) hashTreeRootState(state *pb.BeaconState) ([32]byte, er
fieldRoots[16] = justifiedBitsRoot[:]
// PreviousJustifiedCheckpoint data structure root.
prevCheckRoot, err := checkpointRoot(state.PreviousJustifiedCheckpoint)
prevCheckRoot, err := CheckpointRoot(state.PreviousJustifiedCheckpoint)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute previous justified checkpoint merkleization")
return nil, errors.Wrap(err, "could not compute previous justified checkpoint merkleization")
}
fieldRoots[17] = prevCheckRoot[:]
// CurrentJustifiedCheckpoint data structure root.
currJustRoot, err := checkpointRoot(state.CurrentJustifiedCheckpoint)
currJustRoot, err := CheckpointRoot(state.CurrentJustifiedCheckpoint)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute current justified checkpoint merkleization")
return nil, errors.Wrap(err, "could not compute current justified checkpoint merkleization")
}
fieldRoots[18] = currJustRoot[:]
// FinalizedCheckpoint data structure root.
finalRoot, err := checkpointRoot(state.FinalizedCheckpoint)
finalRoot, err := CheckpointRoot(state.FinalizedCheckpoint)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute finalized checkpoint merkleization")
return nil, errors.Wrap(err, "could not compute finalized checkpoint merkleization")
}
fieldRoots[19] = finalRoot[:]
root, err := bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute full beacon state merkleization")
}
return root, nil
return fieldRoots, nil
}
func forkRoot(fork *pb.Fork) ([32]byte, error) {
// Uint64Root computes the HashTreeRoot Merkleization of
// a simple uint64 value according to the eth2
// Simple Serialize specification.
func Uint64Root(val uint64) [32]byte {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, val)
root := bytesutil.ToBytes32(buf)
return root
}
// ForkRoot computes the HashTreeRoot Merkleization of
// a Fork struct value according to the eth2
// Simple Serialize specification.
func ForkRoot(fork *pb.Fork) ([32]byte, error) {
fieldRoots := make([][]byte, 3)
if fork != nil {
prevRoot := bytesutil.ToBytes32(fork.PreviousVersion)
@ -210,7 +239,10 @@ func forkRoot(fork *pb.Fork) ([32]byte, error) {
return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func checkpointRoot(checkpoint *ethpb.Checkpoint) ([32]byte, error) {
// CheckpointRoot computes the HashTreeRoot Merkleization of
// a Checkpoint struct value according to the eth2
// Simple Serialize specification.
func CheckpointRoot(checkpoint *ethpb.Checkpoint) ([32]byte, error) {
fieldRoots := make([][]byte, 2)
if checkpoint != nil {
epochBuf := make([]byte, 8)
@ -222,7 +254,10 @@ func checkpointRoot(checkpoint *ethpb.Checkpoint) ([32]byte, error) {
return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func historicalRootsRoot(historicalRoots [][]byte) ([32]byte, error) {
// HistoricalRootsRoot computes the HashTreeRoot Merkleization of
// a list of [32]byte historical block roots according to the eth2
// Simple Serialize specification.
func HistoricalRootsRoot(historicalRoots [][]byte) ([32]byte, error) {
result, err := bitwiseMerkleize(historicalRoots, uint64(len(historicalRoots)), params.BeaconConfig().HistoricalRootsLimit)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute historical roots merkleization")
@ -238,7 +273,10 @@ func historicalRootsRoot(historicalRoots [][]byte) ([32]byte, error) {
return mixedLen, nil
}
func slashingsRoot(slashings []uint64) ([32]byte, error) {
// SlashingsRoot computes the HashTreeRoot Merkleization of
// a list of uint64 slashing values according to the eth2
// Simple Serialize specification.
func SlashingsRoot(slashings []uint64) ([32]byte, error) {
slashingMarshaling := make([][]byte, params.BeaconConfig().EpochsPerSlashingsVector)
for i := 0; i < len(slashings) && i < len(slashingMarshaling); i++ {
slashBuf := make([]byte, 8)

View File

@ -7,11 +7,25 @@ import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
func validatorBalancesRoot(balances []uint64) ([32]byte, error) {
// ValidatorRegistryRoot computes the HashTreeRoot Merkleization of
// a list of validator structs according to the eth2
// Simple Serialize specification.
func ValidatorRegistryRoot(vals []*ethpb.Validator) ([32]byte, error) {
if featureconfig.Get().EnableSSZCache {
return cachedHasher.validatorRegistryRoot(vals)
}
return nocachedHasher.validatorRegistryRoot(vals)
}
// ValidatorBalancesRoot computes the HashTreeRoot Merkleization of
// a list of validator uint64 balances according to the eth2
// Simple Serialize specification.
func ValidatorBalancesRoot(balances []uint64) ([32]byte, error) {
balancesMarshaling := make([][]byte, 0)
for i := 0; i < len(balances); i++ {
balanceBuf := make([]byte, 8)