mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-08 02:31:19 +00:00
21fc9853ee
* Encode empty `extra_data` as "0x" and `base_fee_per_gas` as Uint256 in the Beacon API * Fixed parsing of Uint256 in the API * Add missing tests for empty hex & Uint256 fields in `apimiddleware` Co-authored-by: Radosław Kapka <rkapka@wp.pl>
165 lines
4.2 KiB
Go
165 lines
4.2 KiB
Go
package apimiddleware
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
"github.com/wealdtech/go-bytesutil"
|
|
)
|
|
|
|
// processField calls each processor function on any field that has the matching tag set.
|
|
// It is a recursive function.
|
|
func processField(s interface{}, processors []fieldProcessor) error {
|
|
kind := reflect.TypeOf(s).Kind()
|
|
if kind != reflect.Ptr && kind != reflect.Slice && kind != reflect.Array {
|
|
return fmt.Errorf("processing fields of kind '%v' is unsupported", kind)
|
|
}
|
|
|
|
t := reflect.TypeOf(s).Elem()
|
|
v := reflect.Indirect(reflect.ValueOf(s))
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
switch v.Field(i).Kind() {
|
|
case reflect.Slice:
|
|
sliceElem := t.Field(i).Type.Elem()
|
|
kind := sliceElem.Kind()
|
|
// Recursively process slices to struct pointers.
|
|
if kind == reflect.Ptr && sliceElem.Elem().Kind() == reflect.Struct {
|
|
for j := 0; j < v.Field(i).Len(); j++ {
|
|
if err := processField(v.Field(i).Index(j).Interface(), processors); err != nil {
|
|
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
|
|
}
|
|
}
|
|
}
|
|
// Process each string in string slices.
|
|
if kind == reflect.String {
|
|
for _, proc := range processors {
|
|
_, hasTag := t.Field(i).Tag.Lookup(proc.tag)
|
|
if hasTag {
|
|
for j := 0; j < v.Field(i).Len(); j++ {
|
|
if err := proc.f(v.Field(i).Index(j)); err != nil {
|
|
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// Recursively process struct pointers.
|
|
case reflect.Ptr:
|
|
if v.Field(i).Elem().Kind() == reflect.Struct {
|
|
if err := processField(v.Field(i).Interface(), processors); err != nil {
|
|
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
|
|
}
|
|
}
|
|
default:
|
|
field := t.Field(i)
|
|
for _, proc := range processors {
|
|
if _, hasTag := field.Tag.Lookup(proc.tag); hasTag {
|
|
if err := proc.f(v.Field(i)); err != nil {
|
|
return errors.Wrapf(err, "could not process field '%s'", t.Field(i).Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func hexToBase64Processor(v reflect.Value) error {
|
|
if v.String() == "0x" {
|
|
v.SetString("")
|
|
return nil
|
|
}
|
|
b, err := bytesutil.FromHexString(v.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.SetString(base64.StdEncoding.EncodeToString(b))
|
|
return nil
|
|
}
|
|
|
|
func base64ToHexProcessor(v reflect.Value) error {
|
|
if v.String() == "" {
|
|
// Empty hex values are represented as "0x".
|
|
v.SetString("0x")
|
|
return nil
|
|
}
|
|
b, err := base64.StdEncoding.DecodeString(v.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.SetString(hexutil.Encode(b))
|
|
return nil
|
|
}
|
|
|
|
func base64ToUint256Processor(v reflect.Value) error {
|
|
if v.String() == "" {
|
|
return nil
|
|
}
|
|
littleEndian, err := base64.StdEncoding.DecodeString(v.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(littleEndian) != 32 {
|
|
return errors.New("invalid length for Uint256")
|
|
}
|
|
|
|
// Integers are stored as little-endian, but
|
|
// big.Int expects big-endian. So we need to reverse
|
|
// the byte order before decoding.
|
|
var bigEndian [32]byte
|
|
for i := 0; i < len(littleEndian); i++ {
|
|
bigEndian[i] = littleEndian[len(littleEndian)-1-i]
|
|
}
|
|
var uint256 big.Int
|
|
uint256.SetBytes(bigEndian[:])
|
|
v.SetString(uint256.String())
|
|
return nil
|
|
}
|
|
|
|
func uint256ToBase64Processor(v reflect.Value) error {
|
|
if v.String() == "" {
|
|
return nil
|
|
}
|
|
uint256, ok := new(big.Int).SetString(v.String(), 10)
|
|
if !ok {
|
|
return fmt.Errorf("could not parse Uint256")
|
|
}
|
|
bigEndian := uint256.Bytes()
|
|
if len(bigEndian) > 32 {
|
|
return fmt.Errorf("number too big for Uint256")
|
|
}
|
|
|
|
// Integers are stored as little-endian, but
|
|
// big.Int gives big-endian. So we need to reverse
|
|
// the byte order before encoding.
|
|
var littleEndian [32]byte
|
|
for i := 0; i < len(bigEndian); i++ {
|
|
littleEndian[i] = bigEndian[len(bigEndian)-1-i]
|
|
}
|
|
v.SetString(base64.StdEncoding.EncodeToString(littleEndian[:]))
|
|
return nil
|
|
}
|
|
|
|
func enumToLowercaseProcessor(v reflect.Value) error {
|
|
v.SetString(strings.ToLower(v.String()))
|
|
return nil
|
|
}
|
|
|
|
func timeToUnixProcessor(v reflect.Value) error {
|
|
t, err := time.Parse(time.RFC3339, v.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.SetString(strconv.FormatUint(uint64(t.Unix()), 10))
|
|
return nil
|
|
}
|