package fieldtrie import ( "encoding/binary" "fmt" "reflect" "github.com/pkg/errors" customtypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil" "github.com/prysmaticlabs/prysm/beacon-chain/state/types" "github.com/prysmaticlabs/prysm/crypto/hash" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/encoding/ssz" pmath "github.com/prysmaticlabs/prysm/math" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/runtime/version" ) // ProofFromMerkleLayers creates a proof starting at the leaf index of the state Merkle layers. func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex types.FieldIndex) [][]byte { // The merkle tree structure looks as follows: // [[r1, r2, r3, r4], [parent1, parent2], [root]] proof := make([][]byte, 0) currentIndex := startingLeafIndex for i := 0; i < len(layers)-1; i++ { neighborIdx := currentIndex ^ 1 neighbor := layers[i][neighborIdx] proof = append(proof, neighbor) currentIndex = currentIndex / 2 } return proof } func (f *FieldTrie) validateIndices(idxs []uint64) error { length := f.length if f.dataType == types.CompressedArray { comLength, err := f.field.ElemsInChunk() if err != nil { return err } length *= comLength } for _, idx := range idxs { if idx >= length { return errors.Errorf("invalid index for field %s: %d >= length %d", f.field.String(version.Phase0), idx, length) } } return nil } func validateElements(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) error { if dataType == types.CompressedArray { comLength, err := field.ElemsInChunk() if err != nil { return err } length *= comLength } val := reflect.Indirect(reflect.ValueOf(elements)) if uint64(val.Len()) > length { return errors.Errorf("elements length is larger than expected for field %s: %d > %d", field.String(version.Phase0), val.Len(), length) } return nil } // fieldConverters converts the corresponding field and the provided elements to the appropriate roots. func fieldConverters(field types.FieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { switch field { case types.BlockRoots: switch val := elements.(type) { case [][]byte: return handleByteArrays(val, indices, convertAll) case *customtypes.BlockRoots: return handle32ByteArrays(val[:], indices, convertAll) default: return nil, errors.Errorf("Incorrect type used for block roots") } case types.StateRoots: switch val := elements.(type) { case [][]byte: return handleByteArrays(val, indices, convertAll) case *customtypes.StateRoots: return handle32ByteArrays(val[:], indices, convertAll) default: return nil, errors.Errorf("Incorrect type used for state roots") } case types.RandaoMixes: switch val := elements.(type) { case [][]byte: return handleByteArrays(val, indices, convertAll) case *customtypes.RandaoMixes: return handle32ByteArrays(val[:], indices, convertAll) default: return nil, errors.Errorf("Incorrect type used for randao mixes") } case types.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 handleEth1DataSlice(val, indices, convertAll) case types.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 handleValidatorSlice(val, indices, convertAll) case types.PreviousEpochAttestations, types.CurrentEpochAttestations: val, ok := elements.([]*ethpb.PendingAttestation) if !ok { return nil, errors.Errorf("Wanted type of %v but got %v", reflect.TypeOf([]*ethpb.PendingAttestation{}).Name(), reflect.TypeOf(elements).Name()) } return handlePendingAttestationSlice(val, indices, convertAll) case types.Balances: val, ok := elements.([]uint64) if !ok { return nil, errors.Errorf("Wanted type of %v but got %v", reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name()) } return handleBalanceSlice(val, indices, convertAll) default: return [][32]byte{}, errors.Errorf("got unsupported type of %v", reflect.TypeOf(elements).Name()) } } // handleByteArrays computes and returns byte arrays in a slice of root format. func handleByteArrays(val [][]byte, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) if convertAll { length = len(val) } roots := make([][32]byte, 0, length) rootCreator := func(input []byte) { newRoot := bytesutil.ToBytes32(input) roots = append(roots, newRoot) } if convertAll { for i := range val { rootCreator(val[i]) } return roots, nil } if len(val) > 0 { for _, idx := range indices { if idx > uint64(len(val))-1 { return nil, fmt.Errorf("index %d greater than number of byte arrays %d", idx, len(val)) } rootCreator(val[idx]) } } return roots, nil } // handle32ByteArrays computes and returns 32 byte arrays in a slice of root format. func handle32ByteArrays(val [][32]byte, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) if convertAll { length = len(val) } roots := make([][32]byte, 0, length) rootCreator := func(input [32]byte) { roots = append(roots, input) } if convertAll { for i := range val { rootCreator(val[i]) } return roots, nil } if len(val) > 0 { for _, idx := range indices { if idx > uint64(len(val))-1 { return nil, fmt.Errorf("index %d greater than number of byte arrays %d", idx, len(val)) } rootCreator(val[idx]) } } return roots, nil } // handleValidatorSlice returns the validator indices in a slice of root format. func handleValidatorSlice(val []*ethpb.Validator, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) if convertAll { length = len(val) } roots := make([][32]byte, 0, length) hasher := hash.CustomSHA256Hasher() rootCreator := func(input *ethpb.Validator) error { newRoot, err := stateutil.ValidatorRootWithHasher(hasher, input) if err != nil { return err } roots = append(roots, newRoot) return nil } if convertAll { for i := range val { err := rootCreator(val[i]) if err != nil { return nil, err } } return roots, nil } if len(val) > 0 { for _, idx := range indices { if idx > uint64(len(val))-1 { return nil, fmt.Errorf("index %d greater than number of validators %d", idx, len(val)) } err := rootCreator(val[idx]) if err != nil { return nil, err } } } return roots, nil } // handleEth1DataSlice processes a list of eth1data and indices into the appropriate roots. func handleEth1DataSlice(val []*ethpb.Eth1Data, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) if convertAll { length = len(val) } roots := make([][32]byte, 0, length) hasher := hash.CustomSHA256Hasher() rootCreator := func(input *ethpb.Eth1Data) error { newRoot, err := stateutil.Eth1DataRootWithHasher(hasher, input) if err != nil { return err } roots = append(roots, newRoot) return nil } if convertAll { for i := range val { err := rootCreator(val[i]) if err != nil { return nil, err } } return roots, nil } if len(val) > 0 { for _, idx := range indices { if idx > uint64(len(val))-1 { return nil, fmt.Errorf("index %d greater than number of items in eth1 data slice %d", idx, len(val)) } err := rootCreator(val[idx]) if err != nil { return nil, err } } } return roots, nil } // handlePendingAttestationSlice returns the root of a slice of pending attestations. func handlePendingAttestationSlice(val []*ethpb.PendingAttestation, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) if convertAll { length = len(val) } roots := make([][32]byte, 0, length) hasher := hash.CustomSHA256Hasher() rootCreator := func(input *ethpb.PendingAttestation) error { newRoot, err := stateutil.PendingAttRootWithHasher(hasher, input) if err != nil { return err } roots = append(roots, newRoot) return nil } if convertAll { for i := range val { err := rootCreator(val[i]) if err != nil { return nil, err } } return roots, nil } if len(val) > 0 { for _, idx := range indices { if idx > uint64(len(val))-1 { return nil, fmt.Errorf("index %d greater than number of pending attestations %d", idx, len(val)) } err := rootCreator(val[idx]) if err != nil { return nil, err } } } return roots, nil } // handleBalanceSlice returns the root of a slice of validator balances. func handleBalanceSlice(val, indices []uint64, convertAll bool) ([][32]byte, error) { if convertAll { balancesMarshaling := make([][]byte, 0) for _, b := range val { balanceBuf := make([]byte, 8) binary.LittleEndian.PutUint64(balanceBuf, b) balancesMarshaling = append(balancesMarshaling, balanceBuf) } balancesChunks, err := ssz.PackByChunk(balancesMarshaling) if err != nil { return [][32]byte{}, errors.Wrap(err, "could not pack balances into chunks") } return balancesChunks, nil } if len(val) > 0 { numOfElems, err := types.Balances.ElemsInChunk() if err != nil { return nil, err } iNumOfElems, err := pmath.Int(numOfElems) if err != nil { return nil, err } roots := [][32]byte{} for _, idx := range indices { // We split the indexes into their relevant groups. Balances // are compressed according to 4 values -> 1 chunk. startIdx := idx / numOfElems startGroup := startIdx * numOfElems chunk := [32]byte{} sizeOfElem := len(chunk) / iNumOfElems for i, j := 0, startGroup; j < startGroup+numOfElems; i, j = i+sizeOfElem, j+1 { wantedVal := uint64(0) // We are adding chunks in sets of 4, if the set is at the edge of the array // then you will need to zero out the rest of the chunk. Ex : 41 indexes, // so 41 % 4 = 1 . There are 3 indexes, which do not exist yet but we // have to add in as a root. These 3 indexes are then given a 'zero' value. if j < uint64(len(val)) { wantedVal = val[j] } binary.LittleEndian.PutUint64(chunk[i:i+sizeOfElem], wantedVal) } roots = append(roots, chunk) } return roots, nil } return [][32]byte{}, nil }