diff --git a/beacon-chain/state/BUILD.bazel b/beacon-chain/state/BUILD.bazel new file mode 100644 index 000000000000..ba0e4faaf077 --- /dev/null +++ b/beacon-chain/state/BUILD.bazel @@ -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", + ], +) diff --git a/beacon-chain/state/getters.go b/beacon-chain/state/getters.go new file mode 100644 index 000000000000..87c8e97f15b6 --- /dev/null +++ b/beacon-chain/state/getters.go @@ -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, + } +} diff --git a/beacon-chain/state/setters.go b/beacon-chain/state/setters.go new file mode 100644 index 000000000000..aaee74225d2f --- /dev/null +++ b/beacon-chain/state/setters.go @@ -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 +} diff --git a/beacon-chain/state/types.go b/beacon-chain/state/types.go new file mode 100644 index 000000000000..f2ec204cb450 --- /dev/null +++ b/beacon-chain/state/types.go @@ -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 +} diff --git a/beacon-chain/state/types_test.go b/beacon-chain/state/types_test.go new file mode 100644 index 000000000000..53b63bc60c3d --- /dev/null +++ b/beacon-chain/state/types_test.go @@ -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 +} diff --git a/shared/stateutil/arrays.go b/shared/stateutil/arrays.go index 7b0454050d42..f9f688e428bb 100644 --- a/shared/stateutil/arrays.go +++ b/shared/stateutil/arrays.go @@ -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 { diff --git a/shared/stateutil/attestations.go b/shared/stateutil/attestations.go index d435b26beee1..33b7326248a8 100644 --- a/shared/stateutil/attestations.go +++ b/shared/stateutil/attestations.go @@ -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") } diff --git a/shared/stateutil/blocks.go b/shared/stateutil/blocks.go index 653e6e2c866c..30681bef32fe 100644 --- a/shared/stateutil/blocks.go +++ b/shared/stateutil/blocks.go @@ -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") } diff --git a/shared/stateutil/state_root.go b/shared/stateutil/state_root.go index e39ee217b6a9..78af41fba57a 100644 --- a/shared/stateutil/state_root.go +++ b/shared/stateutil/state_root.go @@ -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[:] + return fieldRoots, nil +} - 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 +// 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 } -func forkRoot(fork *pb.Fork) ([32]byte, error) { +// 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) diff --git a/shared/stateutil/validators.go b/shared/stateutil/validators.go index 39633abbfca2..d5cac2523595 100644 --- a/shared/stateutil/validators.go +++ b/shared/stateutil/validators.go @@ -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)