Remove SSZ Cache (#10256)

* remove ssz cache

* gaz

* lint

* analyze more

* fix
This commit is contained in:
Nishant Das
2022-02-22 17:27:51 +08:00
committed by GitHub
parent a55fdf8949
commit 525c818672
20 changed files with 54 additions and 475 deletions

View File

@@ -20,7 +20,7 @@ func TestFieldTrie_NewTrie(t *testing.T) {
// 5 represents the enum value of state roots
trie, err := fieldtrie.NewFieldTrie(5, stateTypes.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
root, err := stateutil.RootsArrayHashTreeRoot(newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot), "StateRoots")
root, err := stateutil.RootsArrayHashTreeRoot(newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
newRoot, err := trie.TrieRoot()
require.NoError(t, err)

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
return stateutil.ComputeFieldRootsWithHasherPhase0(ctx, state)
}

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateAltair) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
return stateutil.ComputeFieldRootsWithHasherAltair(ctx, state)
}

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateBellatrix) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
return stateutil.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}

View File

@@ -32,7 +32,6 @@ go_library(
],
deps = [
"//beacon-chain/core/transition/stateutils:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//container/trie:go_default_library",
@@ -41,7 +40,6 @@ go_library(
"//encoding/ssz:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_dgraph_io_ristretto//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
@@ -55,13 +53,11 @@ go_test(
"field_root_test.go",
"reference_bench_test.go",
"state_root_test.go",
"stateutil_test.go",
"trie_helpers_test.go",
"validator_root_test.go",
],
embed = [":go_default_library"],
deps = [
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//crypto/hash:go_default_library",

View File

@@ -12,27 +12,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// eth1DataEncKey returns the encoded key in bytes of input `eth1Data`,
// the returned key bytes can be used for caching purposes.
func eth1DataEncKey(eth1Data *ethpb.Eth1Data) []byte {
enc := make([]byte, 0, 96)
if eth1Data != nil {
if len(eth1Data.DepositRoot) > 0 {
depRoot := bytesutil.ToBytes32(eth1Data.DepositRoot)
enc = append(enc, depRoot[:]...)
}
eth1DataCountBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(eth1DataCountBuf, eth1Data.DepositCount)
eth1CountRoot := bytesutil.ToBytes32(eth1DataCountBuf)
enc = append(enc, eth1CountRoot[:]...)
if len(eth1Data.BlockHash) > 0 {
blockHash := bytesutil.ToBytes32(eth1Data.BlockHash)
enc = append(enc, blockHash[:]...)
}
}
return enc
}
// Eth1DataRootWithHasher returns the hash tree root of input `eth1Data`.
func Eth1DataRootWithHasher(hasher ssz.HashFn, eth1Data *ethpb.Eth1Data) ([32]byte, error) {
if eth1Data == nil {
@@ -62,22 +41,6 @@ func Eth1DataRootWithHasher(hasher ssz.HashFn, eth1Data *ethpb.Eth1Data) ([32]by
return root, nil
}
// Eth1DatasEncKey returns the encoded key in bytes of input `eth1Data`s,
// the returned key bytes can be used for caching purposes.
func Eth1DatasEncKey(eth1Datas []*ethpb.Eth1Data) ([32]byte, error) {
hasher := hash.CustomSHA256Hasher()
enc := make([]byte, len(eth1Datas)*32)
for i := 0; i < len(eth1Datas); i++ {
eth1, err := Eth1DataRootWithHasher(hasher, eth1Datas[i])
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute eth1data merkleization")
}
copy(enc[(i*32):(i+1)*32], eth1[:])
}
hashKey := hash.FastSum256(enc)
return hashKey, nil
}
// Eth1DatasRoot returns the hash tree root of input `eth1Datas`.
func Eth1DatasRoot(eth1Datas []*ethpb.Eth1Data) ([32]byte, error) {
hasher := hash.CustomSHA256Hasher()

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/ssz"
@@ -15,14 +14,11 @@ import (
// RootsArrayHashTreeRoot computes the Merkle root of arrays of 32-byte hashes, such as [64][32]byte
// according to the Simple Serialize specification of Ethereum.
func RootsArrayHashTreeRoot(vals [][]byte, length uint64, fieldName string) ([32]byte, error) {
if features.Get().EnableSSZCache {
return CachedHasher.arraysRoot(vals, length, fieldName)
}
return NocachedHasher.arraysRoot(vals, length, fieldName)
func RootsArrayHashTreeRoot(vals [][]byte, length uint64) ([32]byte, error) {
return arraysRoot(vals, length)
}
func (h *stateRootHasher) epochAttestationsRoot(atts []*ethpb.PendingAttestation) ([32]byte, error) {
func epochAttestationsRoot(atts []*ethpb.PendingAttestation) ([32]byte, error) {
max := uint64(fieldparams.CurrentEpochAttestationsLength)
if uint64(len(atts)) > max {
return [32]byte{}, fmt.Errorf("epoch attestation exceeds max length %d", max)
@@ -31,7 +27,7 @@ func (h *stateRootHasher) epochAttestationsRoot(atts []*ethpb.PendingAttestation
hasher := hash.CustomSHA256Hasher()
roots := make([][]byte, len(atts))
for i := 0; i < len(atts); i++ {
pendingRoot, err := h.pendingAttestationRoot(hasher, atts[i])
pendingRoot, err := pendingAttestationRoot(hasher, atts[i])
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not attestation merkleization")
}
@@ -58,26 +54,9 @@ func (h *stateRootHasher) epochAttestationsRoot(atts []*ethpb.PendingAttestation
return res, nil
}
func (h *stateRootHasher) pendingAttestationRoot(hasher ssz.HashFn, att *ethpb.PendingAttestation) ([32]byte, error) {
func pendingAttestationRoot(hasher ssz.HashFn, att *ethpb.PendingAttestation) ([32]byte, error) {
if att == nil {
return [32]byte{}, errors.New("nil pending attestation")
}
// Marshal attestation to determine if it exists in the cache.
enc := pendingAttEncKey(att)
// Check if it exists in cache:
if h.rootsCache != nil {
if found, ok := h.rootsCache.Get(string(enc)); found != nil && ok {
return found.([32]byte), nil
}
}
res, err := PendingAttRootWithHasher(hasher, att)
if err != nil {
return [32]byte{}, err
}
if h.rootsCache != nil {
h.rootsCache.Set(string(enc), res, 32)
}
return res, nil
return PendingAttRootWithHasher(hasher, att)
}

View File

@@ -2,7 +2,6 @@ package stateutil
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/features"
"github.com/prysmaticlabs/prysm/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -14,45 +13,12 @@ func Eth1Root(hasher ssz.HashFn, eth1Data *ethpb.Eth1Data) ([32]byte, error) {
if eth1Data == nil {
return [32]byte{}, errors.New("nil eth1 data")
}
enc := eth1DataEncKey(eth1Data)
if features.Get().EnableSSZCache {
if found, ok := CachedHasher.rootsCache.Get(string(enc)); ok && found != nil {
return found.([32]byte), nil
}
}
root, err := Eth1DataRootWithHasher(hasher, eth1Data)
if err != nil {
return [32]byte{}, err
}
if features.Get().EnableSSZCache {
CachedHasher.rootsCache.Set(string(enc), root, 32)
}
return root, nil
return Eth1DataRootWithHasher(hasher, eth1Data)
}
// 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) {
hashKey, err := Eth1DatasEncKey(eth1DataVotes)
if err != nil {
return [32]byte{}, err
}
if features.Get().EnableSSZCache {
if found, ok := CachedHasher.rootsCache.Get(string(hashKey[:])); ok && found != nil {
return found.([32]byte), nil
}
}
root, err := Eth1DatasRoot(eth1DataVotes)
if err != nil {
return [32]byte{}, err
}
if features.Get().EnableSSZCache {
CachedHasher.rootsCache.Set(string(hashKey[:]), root, 32)
}
return root, nil
return Eth1DatasRoot(eth1DataVotes)
}

View File

@@ -7,17 +7,17 @@ import (
)
func TestArraysTreeRoot_OnlyPowerOf2(t *testing.T) {
_, err := NocachedHasher.arraysRoot([][]byte{}, 1, "testing")
_, err := arraysRoot([][]byte{}, 1)
assert.NoError(t, err)
_, err = NocachedHasher.arraysRoot([][]byte{}, 4, "testing")
_, err = arraysRoot([][]byte{}, 4)
assert.NoError(t, err)
_, err = NocachedHasher.arraysRoot([][]byte{}, 8, "testing")
_, err = arraysRoot([][]byte{}, 8)
assert.NoError(t, err)
_, err = NocachedHasher.arraysRoot([][]byte{}, 10, "testing")
_, err = arraysRoot([][]byte{}, 10)
assert.ErrorContains(t, "hash layer is a non power of 2", err)
}
func TestArraysTreeRoot_ZeroLength(t *testing.T) {
_, err := NocachedHasher.arraysRoot([][]byte{}, 0, "testing")
_, err := arraysRoot([][]byte{}, 0)
assert.ErrorContains(t, "zero leaves provided", err)
}

View File

@@ -5,7 +5,6 @@ import (
"encoding/binary"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/ssz"
@@ -16,33 +15,18 @@ import (
// a list of validator structs according to the Ethereum
// Simple Serialize specification.
func ValidatorRegistryRoot(vals []*ethpb.Validator) ([32]byte, error) {
if features.Get().EnableSSZCache {
return CachedHasher.validatorRegistryRoot(vals)
}
return NocachedHasher.validatorRegistryRoot(vals)
return validatorRegistryRoot(vals)
}
func (h *stateRootHasher) validatorRegistryRoot(validators []*ethpb.Validator) ([32]byte, error) {
hashKeyElements := make([]byte, len(validators)*32)
func validatorRegistryRoot(validators []*ethpb.Validator) ([32]byte, error) {
roots := make([][32]byte, len(validators))
emptyKey := hash.FastSum256(hashKeyElements)
hasher := hash.CustomSHA256Hasher()
bytesProcessed := 0
for i := 0; i < len(validators); i++ {
val, err := h.validatorRoot(hasher, validators[i])
val, err := validatorRoot(hasher, validators[i])
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute validators merkleization")
}
copy(hashKeyElements[bytesProcessed:bytesProcessed+32], val[:])
roots[i] = val
bytesProcessed += 32
}
hashKey := hash.FastSum256(hashKeyElements)
if hashKey != emptyKey && h.rootsCache != nil {
if found, ok := h.rootsCache.Get(string(hashKey[:])); found != nil && ok {
return found.([32]byte), nil
}
}
validatorsRootsRoot, err := ssz.BitwiseMerkleizeArrays(hasher, roots, uint64(len(roots)), fieldparams.ValidatorRegistryLimit)
@@ -57,32 +41,13 @@ func (h *stateRootHasher) validatorRegistryRoot(validators []*ethpb.Validator) (
var validatorsRootsBufRoot [32]byte
copy(validatorsRootsBufRoot[:], validatorsRootsBuf.Bytes())
res := ssz.MixInLength(validatorsRootsRoot, validatorsRootsBufRoot[:])
if hashKey != emptyKey && h.rootsCache != nil {
h.rootsCache.Set(string(hashKey[:]), res, 32)
}
return res, nil
}
func (h *stateRootHasher) validatorRoot(hasher ssz.HashFn, validator *ethpb.Validator) ([32]byte, error) {
func validatorRoot(hasher ssz.HashFn, validator *ethpb.Validator) ([32]byte, error) {
if validator == nil {
return [32]byte{}, errors.New("nil validator")
}
enc := validatorEncKey(validator)
// Check if it exists in cache:
if h.rootsCache != nil {
if found, ok := h.rootsCache.Get(string(enc)); found != nil && ok {
return found.([32]byte), nil
}
}
valRoot, err := ValidatorRootWithHasher(hasher, validator)
if err != nil {
return [32]byte{}, err
}
if h.rootsCache != nil {
h.rootsCache.Set(string(enc), valRoot, 32)
}
return valRoot, nil
return ValidatorRootWithHasher(hasher, validator)
}

View File

@@ -6,129 +6,23 @@ import (
"github.com/prysmaticlabs/prysm/encoding/ssz"
)
func (h *stateRootHasher) arraysRoot(input [][]byte, length uint64, fieldName string) ([32]byte, error) {
lock.Lock()
defer lock.Unlock()
func arraysRoot(input [][]byte, length uint64) ([32]byte, error) {
hashFunc := hash.CustomSHA256Hasher()
if _, ok := layersCache[fieldName]; !ok && h.rootsCache != nil {
depth := ssz.Depth(length)
layersCache[fieldName] = make([][][32]byte, depth+1)
}
leaves := make([][32]byte, length)
for i, chunk := range input {
copy(leaves[i][:], chunk)
}
bytesProcessed := 0
changedIndices := make([]int, 0)
prevLeaves, ok := leavesCache[fieldName]
if len(prevLeaves) == 0 || h.rootsCache == nil {
prevLeaves = leaves
}
// Exit early if our previous leaves length don't match with the current set.
// This should never happen but better to be defensive here.
if len(prevLeaves) != len(leaves) {
res, err := h.merkleizeWithCache(leaves, length, fieldName, hashFunc)
if err != nil {
return [32]byte{}, err
}
if h.rootsCache != nil {
leavesCache[fieldName] = leaves
}
return res, nil
}
for i := 0; i < len(leaves); i++ {
// We check if any items changed since the roots were last recomputed.
notEqual := leaves[i] != prevLeaves[i]
if ok && h.rootsCache != nil && notEqual {
changedIndices = append(changedIndices, i)
}
bytesProcessed += 32
}
if len(changedIndices) > 0 && h.rootsCache != nil {
var rt [32]byte
var err error
// If indices did change since last computation, we only recompute
// the modified branches in the cached Merkle tree for this state field.
chunks := leaves
// We need to ensure we recompute indices of the Merkle tree which
// changed in-between calls to this function. This check adds an offset
// to the recomputed indices to ensure we do so evenly.
maxChangedIndex := changedIndices[len(changedIndices)-1]
if maxChangedIndex+2 == len(chunks) && maxChangedIndex%2 != 0 {
changedIndices = append(changedIndices, maxChangedIndex+1)
}
for i := 0; i < len(changedIndices); i++ {
rt, err = recomputeRoot(changedIndices[i], chunks, fieldName, hashFunc)
if err != nil {
return [32]byte{}, err
}
}
leavesCache[fieldName] = chunks
return rt, nil
}
res, err := h.merkleizeWithCache(leaves, length, fieldName, hashFunc)
res, err := merkleize(leaves, length, hashFunc)
if err != nil {
return [32]byte{}, err
}
if h.rootsCache != nil {
leavesCache[fieldName] = leaves
}
return res, nil
}
func recomputeRoot(idx int, chunks [][32]byte, fieldName string, hasher func([]byte) [32]byte) ([32]byte, error) {
items, ok := layersCache[fieldName]
if !ok {
return [32]byte{}, errors.New("could not recompute root as there was no cache found")
}
if items == nil {
return [32]byte{}, errors.New("could not recompute root as there were no items found in the layers cache")
}
layers := items
root := chunks[idx]
layers[0] = chunks
// 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
for i := 0; i < len(layers)-1; i++ {
isLeft := currentIndex%2 == 0
neighborIdx := currentIndex ^ 1
neighbor := [32]byte{}
if layers[i] != nil && len(layers[i]) != 0 && neighborIdx < len(layers[i]) {
neighbor = layers[i][neighborIdx]
}
if isLeft {
parentHash := hasher(append(root[:], neighbor[:]...))
root = parentHash
} else {
parentHash := hasher(append(neighbor[:], root[:]...))
root = parentHash
}
parentIdx := currentIndex / 2
// Update the cached layers at the parent index.
if len(layers[i+1]) == 0 {
layers[i+1] = append(layers[i+1], root)
} else {
layers[i+1][parentIdx] = root
}
currentIndex = parentIdx
}
layersCache[fieldName] = layers
// If there is only a single leaf, we return it (the identity element).
if len(layers[0]) == 1 {
return layers[0][0], nil
}
return root, nil
}
func (h *stateRootHasher) merkleizeWithCache(leaves [][32]byte, length uint64,
fieldName string, hasher func([]byte) [32]byte) ([32]byte, error) {
func merkleize(leaves [][32]byte, length uint64,
hasher func([]byte) [32]byte) ([32]byte, error) {
if len(leaves) == 0 {
return [32]byte{}, errors.New("zero leaves provided")
}
@@ -137,20 +31,12 @@ func (h *stateRootHasher) merkleizeWithCache(leaves [][32]byte, length uint64,
}
hashLayer := leaves
layers := make([][][32]byte, ssz.Depth(length)+1)
if items, ok := layersCache[fieldName]; ok && h.rootsCache != nil {
if len(items[0]) == len(leaves) {
layers = items
}
}
layers[0] = hashLayer
var err error
layers, hashLayer, err = MerkleizeTrieLeaves(layers, hashLayer, hasher)
_, hashLayer, err = MerkleizeTrieLeaves(layers, hashLayer, hasher)
if err != nil {
return [32]byte{}, err
}
root := hashLayer[0]
if h.rootsCache != nil {
layersCache[fieldName] = layers
}
return root, nil
}

View File

@@ -40,29 +40,6 @@ func PendingAttRootWithHasher(hasher ssz.HashFn, att *ethpb.PendingAttestation)
return ssz.BitwiseMerkleizeArrays(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
// pendingAttEncKey returns the encoded key in bytes of input `pendingAttestation`,
// the returned key bytes can be used for caching purposes.
func pendingAttEncKey(att *ethpb.PendingAttestation) []byte {
enc := make([]byte, 2192)
if att != nil {
copy(enc[0:2048], att.AggregationBits)
inclusionBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(inclusionBuf, uint64(att.InclusionDelay))
copy(enc[2048:2056], inclusionBuf)
attDataBuf := marshalAttData(att.Data)
copy(enc[2056:2184], attDataBuf)
proposerBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(proposerBuf, uint64(att.ProposerIndex))
copy(enc[2184:2192], proposerBuf)
}
return enc
}
func attDataRootWithHasher(hasher ssz.HashFn, data *ethpb.AttestationData) ([32]byte, error) {
fieldRoots := make([][]byte, 5)
@@ -100,39 +77,3 @@ func attDataRootWithHasher(hasher ssz.HashFn, data *ethpb.AttestationData) ([32]
return ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
func marshalAttData(data *ethpb.AttestationData) []byte {
enc := make([]byte, 128)
if data != nil {
// Slot.
slotBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(slotBuf, uint64(data.Slot))
copy(enc[0:8], slotBuf)
// Committee index.
indexBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(indexBuf, uint64(data.CommitteeIndex))
copy(enc[8:16], indexBuf)
copy(enc[16:48], data.BeaconBlockRoot)
// Source epoch and root.
if data.Source != nil {
sourceEpochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(sourceEpochBuf, uint64(data.Source.Epoch))
copy(enc[48:56], sourceEpochBuf)
copy(enc[56:88], data.Source.Root)
}
// Target.
if data.Target != nil {
targetEpochBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(targetEpochBuf, uint64(data.Target.Epoch))
copy(enc[88:96], targetEpochBuf)
copy(enc[96:128], data.Target.Root)
}
}
return enc
}

View File

@@ -3,9 +3,7 @@ package stateutil
import (
"context"
"encoding/binary"
"sync"
"github.com/dgraph-io/ristretto"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
@@ -16,45 +14,9 @@ import (
"go.opencensus.io/trace"
)
var (
// Set the map size as equal to that of the latest state field count.
leavesCache = make(map[string][][32]byte, params.BeaconConfig().BeaconStateBellatrixFieldCount)
layersCache = make(map[string][][][32]byte, params.BeaconConfig().BeaconStateBellatrixFieldCount)
lock sync.RWMutex
)
const cacheSize = 100000
// NocachedHasher references a hasher that will not utilize a cache.
var NocachedHasher *stateRootHasher
// CachedHasher references a hasher that will utilize a roots cache.
var CachedHasher *stateRootHasher
func init() {
rootsCache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: cacheSize, // number of keys to track frequency of (1M).
MaxCost: 1 << 22, // maximum cost of cache (3MB).
// 100,000 roots will take up approximately 3 MB in memory.
BufferItems: 64, // number of keys per Get buffer.
})
if err != nil {
panic(err)
}
// Temporarily disable roots cache until cache issues can be resolved.
CachedHasher = &stateRootHasher{rootsCache: rootsCache}
NocachedHasher = &stateRootHasher{}
}
// stateRootHasher defines an object through which we can
// hash the different fields in the state with a few cached layers.
type stateRootHasher struct {
rootsCache *ristretto.Cache
}
// ComputeFieldRootsWithHasherPhase0 hashes the provided phase 0 state and returns its respective field roots.
func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "hasher.ComputeFieldRootsWithHasherPhase0")
func ComputeFieldRootsWithHasherPhase0(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "ComputeFieldRootsWithHasherPhase0")
defer span.End()
if state == nil {
@@ -91,14 +53,14 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context,
fieldRoots[4] = headerHashTreeRoot[:]
// BlockRoots array root.
blockRootsRoot, err := h.arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength, "BlockRoots")
blockRootsRoot, err := arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute block roots merkleization")
}
fieldRoots[5] = blockRootsRoot[:]
// StateRoots array root.
stateRootsRoot, err := h.arraysRoot(state.StateRoots, fieldparams.StateRootsLength, "StateRoots")
stateRootsRoot, err := arraysRoot(state.StateRoots, fieldparams.StateRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute state roots merkleization")
}
@@ -132,7 +94,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context,
fieldRoots[10] = eth1DepositBuf[:]
// Validators slice root.
validatorsRoot, err := h.validatorRegistryRoot(state.Validators)
validatorsRoot, err := validatorRegistryRoot(state.Validators)
if err != nil {
return nil, errors.Wrap(err, "could not compute validator registry merkleization")
}
@@ -146,7 +108,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context,
fieldRoots[12] = balancesRoot[:]
// RandaoMixes array root.
randaoRootsRoot, err := h.arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength, "RandaoMixes")
randaoRootsRoot, err := arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute randao roots merkleization")
}
@@ -160,14 +122,14 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context,
fieldRoots[14] = slashingsRootsRoot[:]
// PreviousEpochAttestations slice root.
prevAttsRoot, err := h.epochAttestationsRoot(state.PreviousEpochAttestations)
prevAttsRoot, err := epochAttestationsRoot(state.PreviousEpochAttestations)
if err != nil {
return nil, errors.Wrap(err, "could not compute previous epoch attestations merkleization")
}
fieldRoots[15] = prevAttsRoot[:]
// CurrentEpochAttestations slice root.
currAttsRoot, err := h.epochAttestationsRoot(state.CurrentEpochAttestations)
currAttsRoot, err := epochAttestationsRoot(state.CurrentEpochAttestations)
if err != nil {
return nil, errors.Wrap(err, "could not compute current epoch attestations merkleization")
}
@@ -201,8 +163,8 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherPhase0(ctx context.Context,
}
// ComputeFieldRootsWithHasherAltair hashes the provided altair state and returns its respective field roots.
func (h *stateRootHasher) ComputeFieldRootsWithHasherAltair(ctx context.Context, state *ethpb.BeaconStateAltair) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "hasher.ComputeFieldRootsWithHasherAltair")
func ComputeFieldRootsWithHasherAltair(ctx context.Context, state *ethpb.BeaconStateAltair) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "ComputeFieldRootsWithHasherAltair")
defer span.End()
if state == nil {
@@ -239,14 +201,14 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherAltair(ctx context.Context,
fieldRoots[4] = headerHashTreeRoot[:]
// BlockRoots array root.
blockRootsRoot, err := h.arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength, "BlockRoots")
blockRootsRoot, err := arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute block roots merkleization")
}
fieldRoots[5] = blockRootsRoot[:]
// StateRoots array root.
stateRootsRoot, err := h.arraysRoot(state.StateRoots, fieldparams.StateRootsLength, "StateRoots")
stateRootsRoot, err := arraysRoot(state.StateRoots, fieldparams.StateRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute state roots merkleization")
}
@@ -280,7 +242,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherAltair(ctx context.Context,
fieldRoots[10] = eth1DepositBuf[:]
// Validators slice root.
validatorsRoot, err := h.validatorRegistryRoot(state.Validators)
validatorsRoot, err := validatorRegistryRoot(state.Validators)
if err != nil {
return nil, errors.Wrap(err, "could not compute validator registry merkleization")
}
@@ -294,7 +256,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherAltair(ctx context.Context,
fieldRoots[12] = balancesRoot[:]
// RandaoMixes array root.
randaoRootsRoot, err := h.arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength, "RandaoMixes")
randaoRootsRoot, err := arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute randao roots merkleization")
}
@@ -371,8 +333,8 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherAltair(ctx context.Context,
}
// ComputeFieldRootsWithHasherBellatrix hashes the provided bellatrix state and returns its respective field roots.
func (h *stateRootHasher) ComputeFieldRootsWithHasherBellatrix(ctx context.Context, state *ethpb.BeaconStateBellatrix) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "hasher.ComputeFieldRootsWithHasherBellatrix")
func ComputeFieldRootsWithHasherBellatrix(ctx context.Context, state *ethpb.BeaconStateBellatrix) ([][]byte, error) {
_, span := trace.StartSpan(ctx, "ComputeFieldRootsWithHasherBellatrix")
defer span.End()
if state == nil {
@@ -409,14 +371,14 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherBellatrix(ctx context.Conte
fieldRoots[4] = headerHashTreeRoot[:]
// BlockRoots array root.
blockRootsRoot, err := h.arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength, "BlockRoots")
blockRootsRoot, err := arraysRoot(state.BlockRoots, fieldparams.BlockRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute block roots merkleization")
}
fieldRoots[5] = blockRootsRoot[:]
// StateRoots array root.
stateRootsRoot, err := h.arraysRoot(state.StateRoots, fieldparams.StateRootsLength, "StateRoots")
stateRootsRoot, err := arraysRoot(state.StateRoots, fieldparams.StateRootsLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute state roots merkleization")
}
@@ -450,7 +412,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherBellatrix(ctx context.Conte
fieldRoots[10] = eth1DepositBuf[:]
// Validators slice root.
validatorsRoot, err := h.validatorRegistryRoot(state.Validators)
validatorsRoot, err := validatorRegistryRoot(state.Validators)
if err != nil {
return nil, errors.Wrap(err, "could not compute validator registry merkleization")
}
@@ -464,7 +426,7 @@ func (h *stateRootHasher) ComputeFieldRootsWithHasherBellatrix(ctx context.Conte
fieldRoots[12] = balancesRoot[:]
// RandaoMixes array root.
randaoRootsRoot, err := h.arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength, "RandaoMixes")
randaoRootsRoot, err := arraysRoot(state.RandaoMixes, fieldparams.RandaoMixesLength)
if err != nil {
return nil, errors.Wrap(err, "could not compute randao roots merkleization")
}

View File

@@ -1,13 +0,0 @@
package stateutil_test
import (
"testing"
"github.com/prysmaticlabs/prysm/config/features"
)
func TestMain(m *testing.M) {
resetCfg := features.InitWithReset(&features.Flags{EnableSSZCache: true})
defer resetCfg()
m.Run()
}

View File

@@ -16,7 +16,7 @@ import (
func TestReturnTrieLayer_OK(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
root, err := stateutil.RootsArrayHashTreeRoot(newState.BlockRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot), "BlockRoots")
root, err := stateutil.RootsArrayHashTreeRoot(newState.BlockRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
blockRts := newState.BlockRoots()
roots := make([][32]byte, 0, len(blockRts))
@@ -63,7 +63,7 @@ func TestRecomputeFromLayer_FixedSizedArray(t *testing.T) {
require.NoError(t, newState.UpdateBlockRootAtIndex(changedIdx[0], changedRoots[0]))
require.NoError(t, newState.UpdateBlockRootAtIndex(changedIdx[1], changedRoots[1]))
expectedRoot, err := stateutil.RootsArrayHashTreeRoot(newState.BlockRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot), "BlockRoots")
expectedRoot, err := stateutil.RootsArrayHashTreeRoot(newState.BlockRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
require.NoError(t, err)
root, _, err := stateutil.RecomputeFromLayer(changedRoots, changedIdx, layers)
require.NoError(t, err)

View File

@@ -87,42 +87,3 @@ func Uint64ListRootWithRegistryLimit(balances []uint64) ([32]byte, error) {
binary.LittleEndian.PutUint64(balancesLengthRoot, uint64(len(balances)))
return ssz.MixInLength(balancesRootsRoot, balancesLengthRoot), nil
}
// validatorEncKey returns the encoded key in bytes of input `validator`,
// the returned key bytes can be used for caching purposes.
func validatorEncKey(validator *ethpb.Validator) []byte {
if validator == nil {
return nil
}
enc := make([]byte, 122)
pubkey := bytesutil.ToBytes48(validator.PublicKey)
copy(enc[0:48], pubkey[:])
withdrawCreds := bytesutil.ToBytes32(validator.WithdrawalCredentials)
copy(enc[48:80], withdrawCreds[:])
effectiveBalanceBuf := [32]byte{}
binary.LittleEndian.PutUint64(effectiveBalanceBuf[:8], validator.EffectiveBalance)
copy(enc[80:88], effectiveBalanceBuf[:8])
if validator.Slashed {
enc[88] = uint8(1)
} else {
enc[88] = uint8(0)
}
activationEligibilityBuf := [32]byte{}
binary.LittleEndian.PutUint64(activationEligibilityBuf[:8], uint64(validator.ActivationEligibilityEpoch))
copy(enc[89:97], activationEligibilityBuf[:8])
activationBuf := [32]byte{}
binary.LittleEndian.PutUint64(activationBuf[:8], uint64(validator.ActivationEpoch))
copy(enc[97:105], activationBuf[:8])
exitBuf := [32]byte{}
binary.LittleEndian.PutUint64(exitBuf[:8], uint64(validator.ExitEpoch))
copy(enc[105:113], exitBuf[:8])
withdrawalBuf := [32]byte{}
binary.LittleEndian.PutUint64(withdrawalBuf[:8], uint64(validator.WithdrawableEpoch))
copy(enc[113:121], withdrawalBuf[:8])
return enc
}

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconState) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherPhase0(ctx, state)
return stateutil.ComputeFieldRootsWithHasherPhase0(ctx, state)
}

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateAltair) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherAltair(ctx, state)
return stateutil.ComputeFieldRootsWithHasherAltair(ctx, state)
}

View File

@@ -4,15 +4,11 @@ import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/config/features"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
// computeFieldRoots returns the hash tree root computations of every field in
// the beacon state as a list of 32 byte roots.
func computeFieldRoots(ctx context.Context, state *ethpb.BeaconStateBellatrix) ([][]byte, error) {
if features.Get().EnableSSZCache {
return stateutil.CachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}
return stateutil.NocachedHasher.ComputeFieldRootsWithHasherBellatrix(ctx, state)
return stateutil.ComputeFieldRootsWithHasherBellatrix(ctx, state)
}

View File

@@ -61,7 +61,6 @@ type Flags struct {
DisableBroadcastSlashings bool // DisableBroadcastSlashings disables p2p broadcasting of proposer and attester slashings.
// Cache toggles.
EnableSSZCache bool // EnableSSZCache see https://github.com/prysmaticlabs/prysm/pull/4558.
EnableActiveBalanceCache bool // EnableActiveBalanceCache enables active balance cache.
// Bug fixes related flags.
@@ -151,8 +150,6 @@ func ConfigureBeaconChain(ctx *cli.Context) {
cfg.WriteSSZStateTransitions = true
}
cfg.EnableSSZCache = true
if ctx.IsSet(disableGRPCConnectionLogging.Name) {
logDisabled(disableGRPCConnectionLogging)
cfg.DisableGRPCConnectionLogs = true