Merge branch 'develop' of github.com:prysmaticlabs/prysm into kintsugi

This commit is contained in:
terence tsao
2022-02-07 12:02:32 -08:00
3 changed files with 290 additions and 63 deletions

View File

@@ -70,6 +70,7 @@ go_library(
"//proto/eth/ext:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
"@go_googleapis//google/api:annotations_go_proto",
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
@@ -88,5 +89,6 @@ go_test(
":go_default_library",
"//encoding/bytesutil:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
],
)

View File

@@ -1,47 +1,215 @@
package enginev1
import "google.golang.org/protobuf/encoding/protojson"
import (
"encoding/binary"
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// HexBytes implements a custom json.Marshaler/Unmarshaler for byte slices that encodes them as
// hex strings per the Ethereum JSON-RPC specification.
type HexBytes []byte
// Quantity implements a custom json.Marshaler/Unmarshaler for uint64 that encodes them as
// big-endian hex strings per the Ethereum JSON-RPC specification.
type Quantity uint64
func (b HexBytes) MarshalJSON() ([]byte, error) {
return json.Marshal(hexutil.Encode(b))
}
func (b *HexBytes) UnmarshalJSON(enc []byte) error {
if len(enc) == 0 {
*b = make([]byte, 0)
return nil
}
var hexString string
if err := json.Unmarshal(enc, &hexString); err != nil {
return err
}
dst, err := hexutil.Decode(hexString)
if err != nil {
return err
}
*b = dst
return nil
}
func (q Quantity) MarshalJSON() ([]byte, error) {
enc := make([]byte, 8)
binary.BigEndian.PutUint64(enc, uint64(q))
return json.Marshal(hexutil.Encode(enc))
}
func (q *Quantity) UnmarshalJSON(enc []byte) error {
if len(enc) == 0 {
*q = 0
return nil
}
var hexString string
if err := json.Unmarshal(enc, &hexString); err != nil {
return err
}
dst, err := hexutil.Decode(hexString)
if err != nil {
return err
}
*q = Quantity(binary.BigEndian.Uint64(dst))
return nil
}
type executionPayloadJSON struct {
ParentHash HexBytes `json:"parentHash"`
FeeRecipient HexBytes `json:"feeRecipient"`
StateRoot HexBytes `json:"stateRoot"`
ReceiptsRoot HexBytes `json:"receiptsRoot"`
LogsBloom HexBytes `json:"logsBloom"`
Random HexBytes `json:"random"`
BlockNumber Quantity `json:"blockNumber"`
GasLimit Quantity `json:"gasLimit"`
GasUsed Quantity `json:"gasUsed"`
Timestamp Quantity `json:"timestamp"`
ExtraData HexBytes `json:"extraData"`
BaseFeePerGas HexBytes `json:"baseFeePerGas"`
BlockHash HexBytes `json:"blockHash"`
Transactions []HexBytes `json:"transactions"`
}
// MarshalJSON defines a custom json.Marshaler interface implementation
// that uses protojson underneath the hood, as protojson will respect
// proper struct tag naming conventions required for the JSON-RPC engine API to work.
// that uses custom json.Marshalers for the HexBytes and Quantity types.
func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
return protojson.Marshal(e)
transactions := make([]HexBytes, len(e.Transactions))
for i, tx := range e.Transactions {
transactions[i] = tx
}
return json.Marshal(executionPayloadJSON{
ParentHash: e.ParentHash,
FeeRecipient: e.FeeRecipient,
StateRoot: e.StateRoot,
ReceiptsRoot: e.ReceiptsRoot,
LogsBloom: e.LogsBloom,
Random: e.Random,
BlockNumber: Quantity(e.BlockNumber),
GasLimit: Quantity(e.GasLimit),
GasUsed: Quantity(e.GasUsed),
Timestamp: Quantity(e.Timestamp),
ExtraData: e.ExtraData,
BaseFeePerGas: e.BaseFeePerGas,
BlockHash: e.BlockHash,
Transactions: transactions,
})
}
// UnmarshalJSON defines a custom json.Unmarshaler interface implementation
// that uses protojson underneath the hood, as protojson will respect
// proper struct tag naming conventions required for the JSON-RPC engine API to work.
// that uses custom json.Unmarshalers for the HexBytes and Quantity types.
func (e *ExecutionPayload) UnmarshalJSON(enc []byte) error {
return protojson.Unmarshal(enc, e)
dec := executionPayloadJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*e = ExecutionPayload{}
e.ParentHash = dec.ParentHash
e.FeeRecipient = dec.FeeRecipient
e.StateRoot = dec.StateRoot
e.ReceiptsRoot = dec.ReceiptsRoot
e.LogsBloom = dec.LogsBloom
e.Random = dec.Random
e.BlockNumber = uint64(dec.BlockNumber)
e.GasLimit = uint64(dec.GasLimit)
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ExtraData = dec.ExtraData
e.BaseFeePerGas = dec.BaseFeePerGas
e.BlockHash = dec.BlockHash
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
transactions[i] = tx
}
e.Transactions = transactions
return nil
}
type payloadAttributesJSON struct {
Timestamp Quantity `json:"timestamp"`
Random HexBytes `json:"random"`
SuggestedFeeRecipient HexBytes `json:"suggestedFeeRecipient"`
}
// MarshalJSON --
func (p *PayloadAttributes) MarshalJSON() ([]byte, error) {
return protojson.Marshal(p)
return json.Marshal(payloadAttributesJSON{
Timestamp: Quantity(p.Timestamp),
Random: p.Random,
SuggestedFeeRecipient: p.SuggestedFeeRecipient,
})
}
// UnmarshalJSON --
func (p *PayloadAttributes) UnmarshalJSON(enc []byte) error {
return protojson.Unmarshal(enc, p)
dec := payloadAttributesJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*p = PayloadAttributes{}
p.Timestamp = uint64(dec.Timestamp)
p.Random = dec.Random
p.SuggestedFeeRecipient = dec.SuggestedFeeRecipient
return nil
}
type payloadStatusJSON struct {
LatestValidHash HexBytes `json:"latestValidHash"`
Status string `json:"status"`
ValidationError string `json:"validationError"`
}
// MarshalJSON --
func (p *PayloadStatus) MarshalJSON() ([]byte, error) {
return protojson.Marshal(p)
return json.Marshal(payloadStatusJSON{
LatestValidHash: p.LatestValidHash,
Status: p.Status.String(),
ValidationError: p.ValidationError,
})
}
// UnmarshalJSON --
func (p *PayloadStatus) UnmarshalJSON(enc []byte) error {
return protojson.Unmarshal(enc, p)
dec := payloadStatusJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*p = PayloadStatus{}
p.LatestValidHash = dec.LatestValidHash
p.Status = PayloadStatus_Status(PayloadStatus_Status_value[dec.Status])
p.ValidationError = dec.ValidationError
return nil
}
type forkchoiceStateJSON struct {
HeadBlockHash HexBytes `json:"headBlockHash"`
SafeBlockHash HexBytes `json:"safeBlockHash"`
FinalizedBlockHash HexBytes `json:"finalizedBlockHash"`
}
// MarshalJSON --
func (f *ForkchoiceState) MarshalJSON() ([]byte, error) {
return protojson.Marshal(f)
return json.Marshal(forkchoiceStateJSON{
HeadBlockHash: f.HeadBlockHash,
SafeBlockHash: f.SafeBlockHash,
FinalizedBlockHash: f.FinalizedBlockHash,
})
}
// UnmarshalJSON --
func (f *ForkchoiceState) UnmarshalJSON(enc []byte) error {
return protojson.Unmarshal(enc, f)
dec := forkchoiceStateJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*f = ForkchoiceState{}
f.HeadBlockHash = dec.HeadBlockHash
f.SafeBlockHash = dec.SafeBlockHash
f.FinalizedBlockHash = dec.FinalizedBlockHash
return nil
}

View File

@@ -2,93 +2,150 @@ package enginev1_test
import (
"encoding/json"
"math"
"testing"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestJsonMarshalUnmarshal(t *testing.T) {
foo := bytesutil.ToBytes32([]byte("foo"))
bar := bytesutil.PadTo([]byte("bar"), 20)
baz := bytesutil.PadTo([]byte("baz"), 256)
t.Run("payload attributes", func(t *testing.T) {
jsonPayload := map[string]interface{}{
"timestamp": 1,
"random": foo[:],
"suggestedFeeRecipient": bar,
jsonPayload := &enginev1.PayloadAttributes{
Timestamp: 1,
Random: []byte("random"),
SuggestedFeeRecipient: []byte("suggestedFeeRecipient"),
}
enc, err := json.Marshal(jsonPayload)
require.NoError(t, err)
payloadPb := &enginev1.PayloadAttributes{}
require.NoError(t, json.Unmarshal(enc, payloadPb))
require.DeepEqual(t, uint64(1), payloadPb.Timestamp)
require.DeepEqual(t, foo[:], payloadPb.Random)
require.DeepEqual(t, bar, payloadPb.SuggestedFeeRecipient)
require.DeepEqual(t, []byte("random"), payloadPb.Random)
require.DeepEqual(t, []byte("suggestedFeeRecipient"), payloadPb.SuggestedFeeRecipient)
})
t.Run("payload status", func(t *testing.T) {
jsonPayload := map[string]interface{}{
"status": "INVALID",
"latestValidHash": foo[:],
"validationError": "failed validation",
jsonPayload := &enginev1.PayloadStatus{
Status: enginev1.PayloadStatus_INVALID,
LatestValidHash: []byte("latestValidHash"),
ValidationError: "failed validation",
}
enc, err := json.Marshal(jsonPayload)
require.NoError(t, err)
payloadPb := &enginev1.PayloadStatus{}
require.NoError(t, json.Unmarshal(enc, payloadPb))
require.DeepEqual(t, "INVALID", payloadPb.Status.String())
require.DeepEqual(t, foo[:], payloadPb.LatestValidHash)
require.DeepEqual(t, []byte("latestValidHash"), payloadPb.LatestValidHash)
require.DeepEqual(t, "failed validation", payloadPb.ValidationError)
})
t.Run("forkchoice state", func(t *testing.T) {
jsonPayload := map[string]interface{}{
"headBlockHash": foo[:],
"safeBlockHash": foo[:],
"finalizedBlockHash": foo[:],
jsonPayload := &enginev1.ForkchoiceState{
HeadBlockHash: []byte("head"),
SafeBlockHash: []byte("safe"),
FinalizedBlockHash: []byte("finalized"),
}
enc, err := json.Marshal(jsonPayload)
require.NoError(t, err)
payloadPb := &enginev1.ForkchoiceState{}
require.NoError(t, json.Unmarshal(enc, payloadPb))
require.DeepEqual(t, foo[:], payloadPb.HeadBlockHash)
require.DeepEqual(t, foo[:], payloadPb.SafeBlockHash)
require.DeepEqual(t, foo[:], payloadPb.FinalizedBlockHash)
require.DeepEqual(t, []byte("head"), payloadPb.HeadBlockHash)
require.DeepEqual(t, []byte("safe"), payloadPb.SafeBlockHash)
require.DeepEqual(t, []byte("finalized"), payloadPb.FinalizedBlockHash)
})
t.Run("execution payload", func(t *testing.T) {
jsonPayload := map[string]interface{}{
"parentHash": foo[:],
"feeRecipient": bar,
"stateRoot": foo[:],
"receiptsRoot": foo[:],
"logsBloom": baz,
"random": foo[:],
"blockNumber": 1,
"gasLimit": 1,
"gasUsed": 1,
"timestamp": 1,
"extraData": foo[:],
"baseFeePerGas": foo[:],
"blockHash": foo[:],
"transactions": [][]byte{foo[:]},
jsonPayload := &enginev1.ExecutionPayload{
ParentHash: []byte("parent"),
FeeRecipient: []byte("feeRecipient"),
StateRoot: []byte("stateRoot"),
ReceiptsRoot: []byte("receiptsRoot"),
LogsBloom: []byte("logsBloom"),
Random: []byte("random"),
BlockNumber: 1,
GasLimit: 2,
GasUsed: 3,
Timestamp: 4,
ExtraData: []byte("extraData"),
BaseFeePerGas: []byte("baseFeePerGas"),
BlockHash: []byte("blockHash"),
Transactions: [][]byte{[]byte("hi")},
}
enc, err := json.Marshal(jsonPayload)
require.NoError(t, err)
payloadPb := &enginev1.ExecutionPayload{}
require.NoError(t, json.Unmarshal(enc, payloadPb))
require.DeepEqual(t, foo[:], payloadPb.ParentHash)
require.DeepEqual(t, bar, payloadPb.FeeRecipient)
require.DeepEqual(t, foo[:], payloadPb.StateRoot)
require.DeepEqual(t, foo[:], payloadPb.ReceiptsRoot)
require.DeepEqual(t, baz, payloadPb.LogsBloom)
require.DeepEqual(t, foo[:], payloadPb.Random)
require.DeepEqual(t, []byte("parent"), payloadPb.ParentHash)
require.DeepEqual(t, []byte("feeRecipient"), payloadPb.FeeRecipient)
require.DeepEqual(t, []byte("stateRoot"), payloadPb.StateRoot)
require.DeepEqual(t, []byte("receiptsRoot"), payloadPb.ReceiptsRoot)
require.DeepEqual(t, []byte("logsBloom"), payloadPb.LogsBloom)
require.DeepEqual(t, []byte("random"), payloadPb.Random)
require.DeepEqual(t, uint64(1), payloadPb.BlockNumber)
require.DeepEqual(t, uint64(1), payloadPb.GasLimit)
require.DeepEqual(t, uint64(1), payloadPb.GasUsed)
require.DeepEqual(t, uint64(1), payloadPb.Timestamp)
require.DeepEqual(t, foo[:], payloadPb.ExtraData)
require.DeepEqual(t, foo[:], payloadPb.BaseFeePerGas)
require.DeepEqual(t, foo[:], payloadPb.BlockHash)
require.DeepEqual(t, [][]byte{foo[:]}, payloadPb.Transactions)
require.DeepEqual(t, uint64(2), payloadPb.GasLimit)
require.DeepEqual(t, uint64(3), payloadPb.GasUsed)
require.DeepEqual(t, uint64(4), payloadPb.Timestamp)
require.DeepEqual(t, []byte("extraData"), payloadPb.ExtraData)
require.DeepEqual(t, []byte("baseFeePerGas"), payloadPb.BaseFeePerGas)
require.DeepEqual(t, []byte("blockHash"), payloadPb.BlockHash)
require.DeepEqual(t, [][]byte{[]byte("hi")}, payloadPb.Transactions)
})
}
func TestHexBytes_MarshalUnmarshalJSON(t *testing.T) {
tests := []struct {
name string
b enginev1.HexBytes
}{
{
name: "empty",
b: []byte{},
},
{
name: "foo",
b: []byte("foo"),
},
{
name: "bytes",
b: []byte{1, 2, 3, 4},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.b.MarshalJSON()
require.NoError(t, err)
var dec enginev1.HexBytes
err = dec.UnmarshalJSON(got)
require.NoError(t, err)
require.DeepEqual(t, tt.b, dec)
})
}
}
func TestQuantity_MarshalUnmarshalJSON(t *testing.T) {
tests := []struct {
name string
b enginev1.Quantity
}{
{
name: "zero",
b: 0,
},
{
name: "num",
b: 5,
},
{
name: "max",
b: math.MaxUint64,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.b.MarshalJSON()
require.NoError(t, err)
var dec enginev1.Quantity
err = dec.UnmarshalJSON(got)
require.NoError(t, err)
require.DeepEqual(t, tt.b, dec)
})
}
}