Update state root cache to fuzz in parallel (#5364)

* Update state root cache to fuzz in parallel

* confirmed arrays passing

Co-authored-by: rauljordan <raul@prysmaticlabs.com>
This commit is contained in:
Preston Van Loon
2020-04-10 11:12:59 -07:00
committed by GitHub
parent 1d8b207d7c
commit 973a0a89b5
4 changed files with 49 additions and 43 deletions

View File

@@ -933,8 +933,8 @@ go_repository(
go_repository(
name = "com_github_google_gofuzz",
importpath = "github.com/google/gofuzz",
sum = "h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=",
version = "v1.0.0",
sum = "h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=",
version = "v1.1.0",
)
go_repository(

View File

@@ -49,6 +49,7 @@ go_test(
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/interop:go_default_library",
"//shared/mputil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_google_gofuzz//:go_default_library",

View File

@@ -25,13 +25,13 @@ func RootsArrayHashTreeRoot(vals [][]byte, length uint64, fieldName string) ([32
}
func (h *stateRootHasher) arraysRoot(input [][]byte, length uint64, fieldName string) ([32]byte, error) {
hashFunc := hashutil.CustomSHA256Hasher()
lock.Lock()
defer lock.Unlock()
hashFunc := hashutil.CustomSHA256Hasher()
if _, ok := layersCache[fieldName]; !ok && h.rootsCache != nil {
depth := GetDepth(length)
layersCache[fieldName] = make([][][32]byte, depth+1)
}
lock.Unlock()
leaves := make([][32]byte, length)
for i, chunk := range input {
@@ -39,9 +39,7 @@ func (h *stateRootHasher) arraysRoot(input [][]byte, length uint64, fieldName st
}
bytesProcessed := 0
changedIndices := make([]int, 0)
lock.RLock()
prevLeaves, ok := leavesCache[fieldName]
lock.RUnlock()
if len(prevLeaves) == 0 || h.rootsCache == nil {
prevLeaves = leaves
}
@@ -74,26 +72,20 @@ func (h *stateRootHasher) arraysRoot(input [][]byte, length uint64, fieldName st
return [32]byte{}, err
}
}
lock.Lock()
leavesCache[fieldName] = chunks
lock.Unlock()
return rt, nil
}
var res [32]byte
res = h.merkleizeWithCache(leaves, length, fieldName, hashFunc)
if h.rootsCache != nil {
lock.Lock()
leavesCache[fieldName] = leaves
lock.Unlock()
}
return res, nil
}
func (h *stateRootHasher) merkleizeWithCache(leaves [][32]byte, length uint64,
fieldName string, hasher func([]byte) [32]byte) [32]byte {
lock.Lock()
defer lock.Unlock()
if len(leaves) == 1 {
var root [32]byte
root = leaves[0]
@@ -144,8 +136,6 @@ func merkleizeTrieLeaves(layers [][][32]byte, hashLayer [][32]byte,
func recomputeRoot(idx int, chunks [][32]byte, length uint64,
fieldName string, hasher func([]byte) [32]byte) ([32]byte, error) {
lock.Lock()
defer lock.Unlock()
items, ok := layersCache[fieldName]
if !ok {
return [32]byte{}, errors.New("could not recompute root as there was no cache found")

View File

@@ -2,10 +2,12 @@ package stateutil
import (
"strconv"
"sync"
"testing"
fuzz "github.com/google/gofuzz"
ethereum_beacon_p2p_v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/mputil"
"github.com/prysmaticlabs/prysm/shared/params"
)
@@ -27,49 +29,62 @@ func TestStateRootCacheFuzz_1000(t *testing.T) {
func fuzzStateRootCache(t *testing.T, seed int64, iterations uint64) {
fuzzer := fuzz.NewWithSeed(seed)
state := &ethereum_beacon_p2p_v1.BeaconState{}
hasher := &stateRootHasher{}
hasherWithCache := cachedHasher
mismatch := 0
mismatchedIndices := make([]uint64, 0)
for i := uint64(0); i < iterations; i++ {
fuzzer.Fuzz(state)
var a, b [32]byte
func() {
defer func() {
if r := recover(); r != nil {
t.Errorf("Non-cached HTR panicked on iteration %d", i)
panic(r)
// Use scatter to run tests in parallel.
if _, err := mputil.Scatter(int(iterations), func(start int, length int, _ *sync.RWMutex) (i interface{}, err error) {
state := &ethereum_beacon_p2p_v1.BeaconState{}
for i := start; i < start+length; i++ {
func() {
defer func() {
recover() // Ignore fuzzing panics for out of range values
}()
fuzzer.Fuzz(state)
}()
var a, b [32]byte
func() {
defer func() {
if r := recover(); r != nil {
t.Errorf("Non-cached HTR panicked on iteration %d", i)
panic(r)
}
}()
var err error
a, err = hasher.hashTreeRootState(state)
if err != nil {
t.Fatal(err)
}
}()
var err error
a, err = hasher.hashTreeRootState(state)
if err != nil {
t.Fatal(err)
}
}()
func() {
defer func() {
if r := recover(); r != nil {
t.Errorf("Cached HTR panicked on iteration %d", i)
panic(r)
func() {
defer func() {
if r := recover(); r != nil {
t.Errorf("Cached HTR panicked on iteration %d", i)
panic(r)
}
}()
var err error
b, err = hasherWithCache.hashTreeRootState(state)
if err != nil {
t.Fatal(err)
}
}()
var err error
b, err = hasherWithCache.hashTreeRootState(state)
if err != nil {
t.Fatal(err)
}
}()
if a != b {
mismatch++
mismatchedIndices = append(mismatchedIndices, i)
if a != b {
mismatch++
mismatchedIndices = append(mismatchedIndices, uint64(i))
}
}
return nil, nil
}); err != nil {
t.Error(err)
}
if mismatch > 0 {
t.Errorf("Mismatched indices: %v", mismatchedIndices)
t.Fatalf("%d of %d random states had different roots", mismatch, iterations)