mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 19:40:37 +00:00
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:
parent
bfda29f2ad
commit
abe679e90e
37
beacon-chain/state/BUILD.bazel
Normal file
37
beacon-chain/state/BUILD.bazel
Normal 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",
|
||||
],
|
||||
)
|
343
beacon-chain/state/getters.go
Normal file
343
beacon-chain/state/getters.go
Normal 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 := ðpb.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 := ðpb.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] = ðpb.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] = ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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 = ðpb.AttestationData{
|
||||
Slot: att.Data.Slot,
|
||||
CommitteeIndex: att.Data.CommitteeIndex,
|
||||
BeaconBlockRoot: beaconRoot[:],
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Source.Epoch,
|
||||
Root: sourceRoot[:],
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: att.Data.Target.Epoch,
|
||||
Root: targetRoot[:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return &pbp2p.PendingAttestation{
|
||||
AggregationBits: aggBits,
|
||||
Data: attData,
|
||||
InclusionDelay: att.InclusionDelay,
|
||||
ProposerIndex: att.ProposerIndex,
|
||||
}
|
||||
}
|
353
beacon-chain/state/setters.go
Normal file
353
beacon-chain/state/setters.go
Normal 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
|
||||
}
|
71
beacon-chain/state/types.go
Normal file
71
beacon-chain/state/types.go
Normal 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
|
||||
}
|
173
beacon-chain/state/types_test.go
Normal file
173
beacon-chain/state/types_test.go
Normal 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, ðpb.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] = ðpb.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] = ðpb.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] = ðpb.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
|
||||
}
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user