2021-06-30 15:06:19 +00:00
|
|
|
package v2
|
2020-03-18 02:09:31 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
2021-07-22 17:13:18 +00:00
|
|
|
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
2021-07-21 21:34:07 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
2020-03-18 02:09:31 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// FieldTrie is the representation of the representative
|
|
|
|
// trie of the particular field.
|
|
|
|
type FieldTrie struct {
|
2021-03-10 23:11:25 +00:00
|
|
|
*sync.RWMutex
|
2021-03-11 02:57:46 +00:00
|
|
|
reference *stateutil.Reference
|
2020-03-18 02:09:31 +00:00
|
|
|
fieldLayers [][]*[32]byte
|
|
|
|
field fieldIndex
|
2021-07-27 15:47:03 +00:00
|
|
|
length uint64
|
2020-03-18 02:09:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFieldTrie is the constructor for the field trie data structure. It creates the corresponding
|
|
|
|
// trie according to the given parameters. Depending on whether the field is a basic/composite array
|
|
|
|
// which is either fixed/variable length, it will appropriately determine the trie.
|
|
|
|
func NewFieldTrie(field fieldIndex, elements interface{}, length uint64) (*FieldTrie, error) {
|
|
|
|
if elements == nil {
|
|
|
|
return &FieldTrie{
|
|
|
|
field: field,
|
2021-03-11 02:57:46 +00:00
|
|
|
reference: stateutil.NewRef(1),
|
2021-03-10 23:11:25 +00:00
|
|
|
RWMutex: new(sync.RWMutex),
|
2021-07-27 15:47:03 +00:00
|
|
|
length: length,
|
2020-03-18 02:09:31 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
datType, ok := fieldMap[field]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("unrecognized field in trie")
|
|
|
|
}
|
|
|
|
fieldRoots, err := fieldConverters(field, []uint64{}, elements, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-07-27 15:47:03 +00:00
|
|
|
if err := validateElements(field, elements, length); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-18 02:09:31 +00:00
|
|
|
switch datType {
|
|
|
|
case basicArray:
|
|
|
|
return &FieldTrie{
|
|
|
|
fieldLayers: stateutil.ReturnTrieLayer(fieldRoots, length),
|
|
|
|
field: field,
|
2021-03-11 02:57:46 +00:00
|
|
|
reference: stateutil.NewRef(1),
|
2021-03-10 23:11:25 +00:00
|
|
|
RWMutex: new(sync.RWMutex),
|
2020-03-18 02:09:31 +00:00
|
|
|
}, nil
|
|
|
|
case compositeArray:
|
|
|
|
return &FieldTrie{
|
|
|
|
fieldLayers: stateutil.ReturnTrieLayerVariable(fieldRoots, length),
|
|
|
|
field: field,
|
2021-03-11 02:57:46 +00:00
|
|
|
reference: stateutil.NewRef(1),
|
2021-03-10 23:11:25 +00:00
|
|
|
RWMutex: new(sync.RWMutex),
|
2020-03-18 02:09:31 +00:00
|
|
|
}, nil
|
|
|
|
default:
|
|
|
|
return nil, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(datType).Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// RecomputeTrie rebuilds the affected branches in the trie according to the provided
|
|
|
|
// changed indices and elements. This recomputes the trie according to the particular
|
|
|
|
// field the trie is based on.
|
|
|
|
func (f *FieldTrie) RecomputeTrie(indices []uint64, elements interface{}) ([32]byte, error) {
|
|
|
|
f.Lock()
|
|
|
|
defer f.Unlock()
|
|
|
|
var fieldRoot [32]byte
|
2020-03-25 06:54:56 +00:00
|
|
|
if len(indices) == 0 {
|
|
|
|
return f.TrieRoot()
|
|
|
|
}
|
2020-03-18 02:09:31 +00:00
|
|
|
datType, ok := fieldMap[f.field]
|
|
|
|
if !ok {
|
|
|
|
return [32]byte{}, errors.Errorf("unrecognized field in trie")
|
|
|
|
}
|
|
|
|
fieldRoots, err := fieldConverters(f.field, indices, elements, false)
|
|
|
|
if err != nil {
|
|
|
|
return [32]byte{}, err
|
|
|
|
}
|
2021-07-27 15:47:03 +00:00
|
|
|
if err := f.validateIndices(indices); err != nil {
|
|
|
|
return [32]byte{}, err
|
|
|
|
}
|
2020-03-18 02:09:31 +00:00
|
|
|
switch datType {
|
|
|
|
case basicArray:
|
|
|
|
fieldRoot, f.fieldLayers, err = stateutil.RecomputeFromLayer(fieldRoots, indices, f.fieldLayers)
|
|
|
|
if err != nil {
|
|
|
|
return [32]byte{}, err
|
|
|
|
}
|
|
|
|
return fieldRoot, nil
|
|
|
|
case compositeArray:
|
|
|
|
fieldRoot, f.fieldLayers, err = stateutil.RecomputeFromLayerVariable(fieldRoots, indices, f.fieldLayers)
|
|
|
|
if err != nil {
|
|
|
|
return [32]byte{}, err
|
|
|
|
}
|
|
|
|
return stateutil.AddInMixin(fieldRoot, uint64(len(f.fieldLayers[0])))
|
|
|
|
default:
|
|
|
|
return [32]byte{}, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(datType).Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopyTrie copies the references to the elements the trie
|
|
|
|
// is built on.
|
|
|
|
func (f *FieldTrie) CopyTrie() *FieldTrie {
|
|
|
|
if f.fieldLayers == nil {
|
|
|
|
return &FieldTrie{
|
|
|
|
field: f.field,
|
2021-03-11 02:57:46 +00:00
|
|
|
reference: stateutil.NewRef(1),
|
2021-03-10 23:11:25 +00:00
|
|
|
RWMutex: new(sync.RWMutex),
|
2020-03-18 02:09:31 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-22 03:39:06 +00:00
|
|
|
dstFieldTrie := make([][]*[32]byte, len(f.fieldLayers))
|
2020-03-18 02:09:31 +00:00
|
|
|
for i, layer := range f.fieldLayers {
|
2020-07-25 18:54:54 +00:00
|
|
|
dstFieldTrie[i] = make([]*[32]byte, len(layer))
|
2020-03-18 02:09:31 +00:00
|
|
|
copy(dstFieldTrie[i], layer)
|
|
|
|
}
|
|
|
|
return &FieldTrie{
|
|
|
|
fieldLayers: dstFieldTrie,
|
|
|
|
field: f.field,
|
2021-03-11 02:57:46 +00:00
|
|
|
reference: stateutil.NewRef(1),
|
2021-03-10 23:11:25 +00:00
|
|
|
RWMutex: new(sync.RWMutex),
|
2020-03-18 02:09:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TrieRoot returns the corresponding root of the trie.
|
|
|
|
func (f *FieldTrie) TrieRoot() ([32]byte, error) {
|
|
|
|
datType, ok := fieldMap[f.field]
|
|
|
|
if !ok {
|
|
|
|
return [32]byte{}, errors.Errorf("unrecognized field in trie")
|
|
|
|
}
|
|
|
|
switch datType {
|
|
|
|
case basicArray:
|
|
|
|
return *f.fieldLayers[len(f.fieldLayers)-1][0], nil
|
|
|
|
case compositeArray:
|
|
|
|
trieRoot := *f.fieldLayers[len(f.fieldLayers)-1][0]
|
|
|
|
return stateutil.AddInMixin(trieRoot, uint64(len(f.fieldLayers[0])))
|
|
|
|
default:
|
|
|
|
return [32]byte{}, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(datType).Name())
|
|
|
|
}
|
|
|
|
}
|
2021-06-30 15:06:19 +00:00
|
|
|
|
|
|
|
// this converts the corresponding field and the provided elements to the appropriate roots.
|
|
|
|
func fieldConverters(field fieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) {
|
|
|
|
switch field {
|
|
|
|
case blockRoots, stateRoots, randaoMixes:
|
|
|
|
val, ok := elements.([][]byte)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("Wanted type of %v but got %v",
|
|
|
|
reflect.TypeOf([][]byte{}).Name(), reflect.TypeOf(elements).Name())
|
|
|
|
}
|
|
|
|
return stateutil.HandleByteArrays(val, indices, convertAll)
|
|
|
|
case eth1DataVotes:
|
|
|
|
val, ok := elements.([]*ethpb.Eth1Data)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("Wanted type of %v but got %v",
|
|
|
|
reflect.TypeOf([]*ethpb.Eth1Data{}).Name(), reflect.TypeOf(elements).Name())
|
|
|
|
}
|
|
|
|
return v1.HandleEth1DataSlice(val, indices, convertAll)
|
|
|
|
case validators:
|
|
|
|
val, ok := elements.([]*ethpb.Validator)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("Wanted type of %v but got %v",
|
|
|
|
reflect.TypeOf([]*ethpb.Validator{}).Name(), reflect.TypeOf(elements).Name())
|
|
|
|
}
|
|
|
|
return stateutil.HandleValidatorSlice(val, indices, convertAll)
|
|
|
|
default:
|
|
|
|
return [][32]byte{}, errors.Errorf("got unsupported type of %v", reflect.TypeOf(elements).Name())
|
|
|
|
}
|
|
|
|
}
|
2021-07-27 15:47:03 +00:00
|
|
|
|
|
|
|
func (f *FieldTrie) validateIndices(idxs []uint64) error {
|
|
|
|
for _, idx := range idxs {
|
|
|
|
if idx >= f.length {
|
|
|
|
return errors.Errorf("invalid index for field %s: %d >= length %d", f.field.String(), idx, f.length)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateElements(field fieldIndex, elements interface{}, length uint64) error {
|
|
|
|
val := reflect.ValueOf(elements)
|
|
|
|
if val.Len() > int(length) {
|
|
|
|
return errors.Errorf("elements length is larger than expected for field %s: %d > %d", field.String(), val.Len(), length)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|