Compare commits

...

60 Commits

Author SHA1 Message Date
Raul Jordan
6fac371616 deepsource 2022-02-08 22:24:58 -05:00
Raul Jordan
2dd2f7b59b confirmed passing 2022-02-08 18:03:51 -05:00
Raul Jordan
09a257af5c passes 2022-02-08 17:57:07 -05:00
Raul Jordan
bfe8941dde use big ints 2022-02-08 17:39:24 -05:00
Raul Jordan
09180cab28 merge engine client 2022-02-08 16:02:19 -05:00
Raul Jordan
7fd4a7f3e0 Merge branch 'develop' into exec-engine-implement 2022-02-08 15:46:31 -05:00
Raul Jordan
3744481132 proper marshal 2022-02-08 14:08:41 -05:00
Raul Jordan
86a82c59e6 gaz 2022-02-08 13:05:20 -06:00
Raul Jordan
362a6f66de proper json encode decode for payload id 2022-02-08 14:03:55 -05:00
Raul Jordan
072b65e564 payload id bytes 2022-02-08 14:00:06 -05:00
Raul Jordan
398e78992d http tests passing 2022-02-08 13:52:43 -05:00
Raul Jordan
1b380b6105 begin tests 2022-02-08 13:47:41 -05:00
Raul Jordan
61963c7d5e exec block by hash 2022-02-08 13:38:35 -05:00
Raul Jordan
520637e225 Merge branch 'develop' into exec-engine-implement 2022-02-08 13:06:30 -05:00
Raul Jordan
2213fa9322 Merge branch 'develop' into exec-engine-implement 2022-02-08 10:02:39 -05:00
Raul Jordan
e448296acf Merge branch 'develop' into exec-engine-implement 2022-02-07 23:45:53 -05:00
Raul Jordan
25a0c50ae2 Merge branch 'exec-engine-implement' of github.com:prysmaticlabs/prysm into exec-engine-implement 2022-02-07 19:27:06 -05:00
Raul Jordan
d911da694f item 2022-02-07 19:27:01 -05:00
Raul Jordan
a146245c23 gaz 2022-02-07 13:09:26 -06:00
Raul Jordan
5a308b71c8 deepsource 2022-02-07 13:52:10 -05:00
Raul Jordan
cb343d586d client tests passing 2022-02-07 13:21:39 -05:00
Raul Jordan
501282cb9f Merge branch 'develop' into exec-engine-implement 2022-02-07 12:23:31 -05:00
Raul Jordan
f705134ba4 sync 2022-02-07 12:23:26 -05:00
Raul Jordan
588aafaf06 http tests 2022-02-04 16:38:33 -05:00
Raul Jordan
ee94b939e1 fix conf 2022-02-04 16:13:43 -05:00
Raul Jordan
c06367e0af confs 2022-02-04 16:13:23 -05:00
Raul Jordan
928d60c5c5 Merge branch 'develop' into exec-engine-implement 2022-02-04 16:12:33 -05:00
Raul Jordan
19a476f767 data 2022-02-04 16:06:54 -05:00
Raul Jordan
91490c7f65 gaz 2022-02-04 16:05:30 -05:00
Raul Jordan
fe90a9ba9e Merge branch 'engine-custom-marshalings' of github.com:prysmaticlabs/prysm into engine-custom-marshalings 2022-02-04 16:03:36 -05:00
Raul Jordan
c43bfc6614 comment 2022-02-04 16:03:26 -05:00
Raul Jordan
181bc56476 rem 2022-02-04 14:59:27 -06:00
Raul Jordan
3a567ccbdd rem 2022-02-04 14:56:59 -06:00
Raul Jordan
700ddb3fb3 hexbytes 2022-02-04 15:56:31 -05:00
Raul Jordan
bea6022ff4 tests passing 2022-02-04 15:37:33 -05:00
Raul Jordan
33e41d6a8a define marshal methods 2022-02-04 13:39:09 -05:00
Raul Jordan
a4e966f890 regen 2022-02-04 12:26:06 -06:00
Raul Jordan
948d30f691 revert build files 2022-02-04 13:24:52 -05:00
Raul Jordan
1f79b34d13 more alias magic 2022-02-04 13:24:36 -05:00
Raul Jordan
3c91ce1358 revert proto 2022-02-04 13:17:29 -05:00
Raul Jordan
f57dd0c268 alias magic 2022-02-04 13:16:58 -05:00
Raul Jordan
d10f761b91 regen 2022-02-04 11:20:00 -06:00
Raul Jordan
fe8e82363d regen 2022-02-04 11:19:42 -06:00
Raul Jordan
f5f6e23432 bytes hash 2022-02-04 11:18:52 -06:00
Raul Jordan
f207887300 regen 2022-02-04 11:17:21 -06:00
Raul Jordan
be274b16e7 bytes hash 2022-02-04 11:16:42 -06:00
Raul Jordan
90010b7db7 only root types 2022-02-04 11:16:09 -06:00
Raul Jordan
5761a44da2 regen 2022-02-04 11:10:22 -06:00
Raul Jordan
ff819037bd add to build file 2022-02-04 11:09:31 -06:00
Raul Jordan
2fefd798cd builds 2022-02-04 11:07:05 -06:00
Raul Jordan
a3f5422f84 builds 2022-02-04 10:58:03 -06:00
Raul Jordan
54d9595dc0 marshalers 2022-02-04 11:55:34 -05:00
Raul Jordan
56f856c8ce custom type 2022-02-04 10:51:06 -06:00
Raul Jordan
e5b41dd8ed gaz 2022-02-04 10:25:32 -06:00
Raul Jordan
1fc9006294 custom type marshaling 2022-02-04 11:23:16 -05:00
Raul Jordan
3a5f4f7451 begin http client test 2022-02-03 16:27:27 -05:00
Raul Jordan
cd5de3e203 ensure error handling works 2022-02-03 16:20:46 -05:00
Raul Jordan
290daf2e78 marshaling issues 2022-02-03 14:50:56 -05:00
Raul Jordan
fdf1b9756b handle real fixtures 2022-02-03 14:38:53 -05:00
Raul Jordan
0616f953e0 add in fixtures 2022-02-03 14:23:14 -05:00
5 changed files with 720 additions and 188 deletions

View File

@@ -1,4 +1,4 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@@ -11,6 +11,21 @@ go_library(
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//proto/engine/v1:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["client_test.go"],
embed = [":go_default_library"],
deps = [
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],

View File

@@ -8,6 +8,7 @@ import (
"net/url"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
@@ -20,6 +21,10 @@ const (
ForkchoiceUpdatedMethod = "engine_forkchoiceUpdatedV1"
// GetPayloadMethod v1 request string for JSON-RPC.
GetPayloadMethod = "engine_getPayloadV1"
// ExecutionBlockByHashMethod request string for JSON-RPC.
ExecutionBlockByHashMethod = "eth_blockByHash"
// LatestExecutionBlockMethod request string for JSON-RPC.
LatestExecutionBlockMethod = "eth_blockByNumber"
// DefaultTimeout for HTTP.
DefaultTimeout = time.Second * 5
)
@@ -27,8 +32,8 @@ const (
// ForkchoiceUpdatedResponse is the response kind received by the
// engine_forkchoiceUpdatedV1 endpoint.
type ForkchoiceUpdatedResponse struct {
Status *pb.PayloadStatus `json:"status"`
PayloadId [8]byte `json:"payloadId"`
Status *pb.PayloadStatus `json:"status"`
PayloadId *pb.PayloadIDBytes `json:"payloadId"`
}
// Client defines a new engine API client for the Prysm consensus node
@@ -67,19 +72,75 @@ func New(ctx context.Context, endpoint string, opts ...Option) (*Client, error)
return c, nil
}
// NewPayload --
func (*Client) NewPayload(_ context.Context, _ *pb.ExecutionPayload) (*pb.PayloadStatus, error) {
return nil, errors.New("unimplemented")
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
func (c *Client) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) (*pb.PayloadStatus, error) {
result := &pb.PayloadStatus{}
err := c.rpc.CallContext(ctx, result, NewPayloadMethod, payload)
return result, handleRPCError(err)
}
// ForkchoiceUpdated --
func (*Client) ForkchoiceUpdated(
_ context.Context, _ *pb.ForkchoiceState, _ *pb.PayloadAttributes,
// ForkchoiceUpdated calls the engine_forkchoiceUpdatedV1 method via JSON-RPC.
func (c *Client) ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes,
) (*ForkchoiceUpdatedResponse, error) {
return nil, errors.New("unimplemented")
result := &ForkchoiceUpdatedResponse{}
err := c.rpc.CallContext(ctx, result, ForkchoiceUpdatedMethod, state, attrs)
return result, handleRPCError(err)
}
// GetPayload --
func (*Client) GetPayload(_ context.Context, _ [8]byte) (*pb.ExecutionPayload, error) {
return nil, errors.New("unimplemented")
// GetPayload calls the engine_getPayloadV1 method via JSON-RPC.
func (c *Client) GetPayload(ctx context.Context, payloadId [8]byte) (*pb.ExecutionPayload, error) {
result := &pb.ExecutionPayload{}
err := c.rpc.CallContext(ctx, result, GetPayloadMethod, pb.PayloadIDBytes(payloadId))
return result, handleRPCError(err)
}
// LatestExecutionBlock fetches the latest execution engine block by calling
// eth_blockByNumber via JSON-RPC.
func (c *Client) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock, error) {
result := &pb.ExecutionBlock{}
err := c.rpc.CallContext(ctx, result, LatestExecutionBlockMethod)
return result, handleRPCError(err)
}
// ExecutionBlockByHash fetches an execution engine block by hash by calling
// eth_blockByHash via JSON-RPC.
func (c *Client) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error) {
result := &pb.ExecutionBlock{}
err := c.rpc.CallContext(ctx, result, ExecutionBlockByHashMethod, hash)
return result, handleRPCError(err)
}
// Handles errors received from the RPC server according to the specification.
func handleRPCError(err error) error {
if err == nil {
return nil
}
e, ok := err.(rpc.Error)
if !ok {
return errors.Wrap(err, "got an unexpected error")
}
switch e.ErrorCode() {
case -32700:
return ErrParse
case -32600:
return ErrInvalidRequest
case -32601:
return ErrMethodNotFound
case -32602:
return ErrInvalidParams
case -32603:
return ErrInternal
case -32001:
return ErrUnknownPayload
case -32000:
// Only -32000 status codes are data errors in the RPC specification.
errWithData, ok := err.(rpc.DataError)
if !ok {
return errors.Wrap(err, "got an unexpected error")
}
return errors.Wrapf(ErrServer, "%v", errWithData.ErrorData())
default:
return err
}
}

View File

@@ -0,0 +1,511 @@
package v1
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestClient_IPC(t *testing.T) {
server := newTestIPCServer(t)
defer server.Stop()
rpcClient := rpc.DialInProc(server)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
ctx := context.Background()
fix := fixtures()
t.Run(GetPayloadMethod, func(t *testing.T) {
want, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
payloadId := [8]byte{1}
resp, err := client.GetPayload(ctx, payloadId)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ForkchoiceUpdatedMethod, func(t *testing.T) {
want, ok := fix["ForkchoiceUpdatedResponse"].(*ForkchoiceUpdatedResponse)
require.Equal(t, true, ok)
resp, err := client.ForkchoiceUpdated(ctx, &pb.ForkchoiceState{}, &pb.PayloadAttributes{})
require.NoError(t, err)
require.DeepEqual(t, want.Status, resp.Status)
require.DeepEqual(t, want.PayloadId, resp.PayloadId)
})
t.Run(NewPayloadMethod, func(t *testing.T) {
want, ok := fix["PayloadStatus"].(*pb.PayloadStatus)
require.Equal(t, true, ok)
req, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
resp, err := client.NewPayload(ctx, req)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(LatestExecutionBlockMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
resp, err := client.LatestExecutionBlock(ctx)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ExecutionBlockByHashMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
arg := common.BytesToHash([]byte("foo"))
resp, err := client.ExecutionBlockByHash(ctx, arg)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
}
func TestClient_HTTP(t *testing.T) {
ctx := context.Background()
fix := fixtures()
t.Run(GetPayloadMethod, func(t *testing.T) {
payloadId := [8]byte{1}
want, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
enc, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
jsonRequestString := string(enc)
reqArg, err := json.Marshal(pb.PayloadIDBytes(payloadId))
require.NoError(t, err)
fmt.Println(jsonRequestString)
// We expect the JSON string RPC request contains the right arguments.
require.Equal(t, true, strings.Contains(
jsonRequestString, string(reqArg),
))
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err = json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.GetPayload(ctx, payloadId)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ForkchoiceUpdatedMethod, func(t *testing.T) {
forkChoiceState := &pb.ForkchoiceState{
HeadBlockHash: []byte("head"),
SafeBlockHash: []byte("safe"),
FinalizedBlockHash: []byte("finalized"),
}
payloadAttributes := &pb.PayloadAttributes{
Timestamp: 1,
Random: []byte("random"),
SuggestedFeeRecipient: []byte("suggestedFeeRecipient"),
}
want, ok := fix["ForkchoiceUpdatedResponse"].(*ForkchoiceUpdatedResponse)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
enc, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
jsonRequestString := string(enc)
forkChoiceStateReq, err := json.Marshal(forkChoiceState)
require.NoError(t, err)
payloadAttrsReq, err := json.Marshal(payloadAttributes)
require.NoError(t, err)
// We expect the JSON string RPC request contains the right arguments.
require.Equal(t, true, strings.Contains(
jsonRequestString, string(forkChoiceStateReq),
))
require.Equal(t, true, strings.Contains(
jsonRequestString, string(payloadAttrsReq),
))
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err = json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.ForkchoiceUpdated(ctx, forkChoiceState, payloadAttributes)
require.NoError(t, err)
require.DeepEqual(t, want.Status, resp.Status)
require.DeepEqual(t, want.PayloadId, resp.PayloadId)
})
t.Run(NewPayloadMethod, func(t *testing.T) {
execPayload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
want, ok := fix["PayloadStatus"].(*pb.PayloadStatus)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
enc, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
jsonRequestString := string(enc)
reqArg, err := json.Marshal(execPayload)
require.NoError(t, err)
// We expect the JSON string RPC request contains the right arguments.
require.Equal(t, true, strings.Contains(
jsonRequestString, string(reqArg),
))
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err = json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(LatestExecutionBlockMethod, func(t *testing.T) {
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err := json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.LatestExecutionBlock(ctx)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
t.Run(ExecutionBlockByHashMethod, func(t *testing.T) {
arg := common.BytesToHash([]byte("foo"))
want, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
require.Equal(t, true, ok)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
enc, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
jsonRequestString := string(enc)
// We expect the JSON string RPC request contains the right arguments.
require.Equal(t, true, strings.Contains(
jsonRequestString, fmt.Sprintf("%#x", arg),
))
resp := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": want,
}
err = json.NewEncoder(w).Encode(resp)
require.NoError(t, err)
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
client := &Client{}
client.rpc = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.ExecutionBlockByHash(ctx, arg)
require.NoError(t, err)
require.DeepEqual(t, want, resp)
})
}
type customError struct {
code int
}
func (c *customError) ErrorCode() int {
return c.code
}
func (*customError) Error() string {
return "something went wrong"
}
type dataError struct {
code int
data interface{}
}
func (c *dataError) ErrorCode() int {
return c.code
}
func (*dataError) Error() string {
return "something went wrong"
}
func (c *dataError) ErrorData() interface{} {
return c.data
}
func Test_handleRPCError(t *testing.T) {
got := handleRPCError(nil)
require.Equal(t, true, got == nil)
var tests = []struct {
name string
expected error
expectedContains string
given error
}{
{
name: "not an rpc error",
expectedContains: "got an unexpected error",
given: errors.New("foo"),
},
{
name: "ErrParse",
expectedContains: ErrParse.Error(),
given: &customError{code: -32700},
},
{
name: "ErrInvalidRequest",
expectedContains: ErrInvalidRequest.Error(),
given: &customError{code: -32600},
},
{
name: "ErrMethodNotFound",
expectedContains: ErrMethodNotFound.Error(),
given: &customError{code: -32601},
},
{
name: "ErrInvalidParams",
expectedContains: ErrInvalidParams.Error(),
given: &customError{code: -32602},
},
{
name: "ErrInternal",
expectedContains: ErrInternal.Error(),
given: &customError{code: -32603},
},
{
name: "ErrUnknownPayload",
expectedContains: ErrUnknownPayload.Error(),
given: &customError{code: -32001},
},
{
name: "ErrServer unexpected no data",
expectedContains: "got an unexpected error",
given: &customError{code: -32000},
},
{
name: "ErrServer with data",
expectedContains: ErrServer.Error(),
given: &dataError{code: -32000, data: 5},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := handleRPCError(tt.given)
require.ErrorContains(t, tt.expectedContains, got)
})
}
}
func newTestIPCServer(t *testing.T) *rpc.Server {
server := rpc.NewServer()
err := server.RegisterName("engine", new(testEngineService))
require.NoError(t, err)
err = server.RegisterName("eth", new(testEngineService))
require.NoError(t, err)
return server
}
func fixtures() map[string]interface{} {
foo := bytesutil.ToBytes32([]byte("foo"))
bar := bytesutil.PadTo([]byte("bar"), 20)
baz := bytesutil.PadTo([]byte("baz"), 256)
executionPayloadFixture := &pb.ExecutionPayload{
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[:]},
}
executionBlock := &pb.ExecutionBlock{
Number: []byte("100"),
Hash: []byte("hash"),
ParentHash: []byte("parentHash"),
Sha3Uncles: []byte("sha3Uncles"),
Miner: []byte("miner"),
StateRoot: []byte("sha3Uncles"),
TransactionsRoot: []byte("transactionsRoot"),
ReceiptsRoot: []byte("receiptsRoot"),
LogsBloom: []byte("logsBloom"),
Difficulty: []byte("1"),
TotalDifficulty: []byte("2"),
GasLimit: 3,
GasUsed: 4,
Timestamp: 5,
Size: []byte("6"),
ExtraData: []byte("extraData"),
BaseFeePerGas: []byte("baseFeePerGas"),
Transactions: [][]byte{foo[:]},
Uncles: [][]byte{foo[:]},
}
status := &pb.PayloadStatus{
Status: pb.PayloadStatus_ACCEPTED,
LatestValidHash: foo[:],
ValidationError: "",
}
id := pb.PayloadIDBytes([8]byte{1, 0, 0, 0, 0, 0, 0, 0})
forkChoiceResp := &ForkchoiceUpdatedResponse{
Status: status,
PayloadId: &id,
}
return map[string]interface{}{
"ExecutionBlock": executionBlock,
"ExecutionPayload": executionPayloadFixture,
"PayloadStatus": status,
"ForkchoiceUpdatedResponse": forkChoiceResp,
}
}
type testEngineService struct{}
func (*testEngineService) NoArgsRets() {}
func (*testEngineService) BlockByHash(
_ context.Context, _ common.Hash,
) *pb.ExecutionBlock {
fix := fixtures()
item, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
if !ok {
panic("not found")
}
return item
}
func (*testEngineService) BlockByNumber(
_ context.Context, _ *big.Int,
) *pb.ExecutionBlock {
fix := fixtures()
item, ok := fix["ExecutionBlock"].(*pb.ExecutionBlock)
if !ok {
panic("not found")
}
return item
}
func (*testEngineService) GetPayloadV1(
_ context.Context, _ pb.PayloadIDBytes,
) *pb.ExecutionPayload {
fix := fixtures()
item, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
if !ok {
panic("not found")
}
return item
}
func (*testEngineService) ForkchoiceUpdatedV1(
_ context.Context, _ *pb.ForkchoiceState, _ *pb.PayloadAttributes,
) *ForkchoiceUpdatedResponse {
fix := fixtures()
item, ok := fix["ForkchoiceUpdatedResponse"].(*ForkchoiceUpdatedResponse)
if !ok {
panic("not found")
}
return item
}
func (*testEngineService) NewPayloadV1(
_ context.Context, _ *pb.ExecutionPayload,
) *pb.PayloadStatus {
fix := fixtures()
item, ok := fix["PayloadStatus"].(*pb.PayloadStatus)
if !ok {
panic("not found")
}
return item
}

View File

@@ -1,105 +1,91 @@
package enginev1
import (
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"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
// PayloadIDBytes defines a custom type for Payload IDs used by the engine API
// client with proper JSON Marshal and Unmarshal methods to hex.
type PayloadIDBytes [8]byte
// MarshalJSON --
func (b HexBytes) MarshalJSON() ([]byte, error) {
return json.Marshal(hexutil.Encode(b))
func (b PayloadIDBytes) MarshalJSON() ([]byte, error) {
return json.Marshal(hexutil.Bytes(b[:]))
}
// UnmarshalJSON --
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 {
func (b *PayloadIDBytes) UnmarshalJSON(enc []byte) error {
hexBytes := hexutil.Bytes(make([]byte, 0))
if err := json.Unmarshal(enc, &hexBytes); err != nil {
return err
}
dst, err := hexutil.Decode(hexString)
if err != nil {
return err
}
*b = dst
return nil
}
// MarshalJSON --
func (q Quantity) MarshalJSON() ([]byte, error) {
enc := make([]byte, 8)
binary.BigEndian.PutUint64(enc, uint64(q))
return json.Marshal(hexutil.Encode(enc))
}
// UnmarshalJSON --
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))
res := [8]byte{}
copy(res[:], hexBytes)
*b = res
return nil
}
type executionBlockJSON struct {
Number HexBytes `json:"number"`
Hash HexBytes `json:"hash"`
ParentHash HexBytes `json:"parentHash"`
Sha3Uncles HexBytes `json:"sha3Uncles"`
Miner HexBytes `json:"miner"`
StateRoot HexBytes `json:"stateRoot"`
TransactionsRoot HexBytes `json:"transactionsRoot"`
ReceiptsRoot HexBytes `json:"receiptsRoot"`
LogsBloom HexBytes `json:"logsBloom"`
Difficulty HexBytes `json:"difficulty"`
TotalDifficulty HexBytes `json:"totalDifficulty"`
GasLimit Quantity `json:"gasLimit"`
GasUsed Quantity `json:"gasUsed"`
Timestamp Quantity `json:"timestamp"`
BaseFeePerGas HexBytes `json:"baseFeePerGas"`
ExtraData HexBytes `json:"extraData"`
MixHash HexBytes `json:"mixHash"`
Nonce HexBytes `json:"nonce"`
Size HexBytes `json:"size"`
Transactions []HexBytes `json:"transactions"`
Uncles []HexBytes `json:"uncles"`
Number *big.Int `json:"number"`
Hash hexutil.Bytes `json:"hash"`
ParentHash hexutil.Bytes `json:"parentHash"`
Sha3Uncles hexutil.Bytes `json:"sha3Uncles"`
Miner hexutil.Bytes `json:"miner"`
StateRoot hexutil.Bytes `json:"stateRoot"`
TransactionsRoot hexutil.Bytes `json:"transactionsRoot"`
ReceiptsRoot hexutil.Bytes `json:"receiptsRoot"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
Difficulty *big.Int `json:"difficulty"`
TotalDifficulty *big.Int `json:"totalDifficulty"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Timestamp hexutil.Uint64 `json:"timestamp"`
BaseFeePerGas *big.Int `json:"baseFeePerGas"`
ExtraData hexutil.Bytes `json:"extraData"`
MixHash hexutil.Bytes `json:"mixHash"`
Nonce hexutil.Bytes `json:"nonce"`
Size *big.Int `json:"size"`
Transactions []hexutil.Bytes `json:"transactions"`
Uncles []hexutil.Bytes `json:"uncles"`
}
// MarshalJSON defines a custom json.Marshaler interface implementation
// that uses custom json.Marshalers for the HexBytes and Quantity types.
// that uses custom json.Marshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlock) MarshalJSON() ([]byte, error) {
transactions := make([]HexBytes, len(e.Transactions))
transactions := make([]hexutil.Bytes, len(e.Transactions))
for i, tx := range e.Transactions {
transactions[i] = tx
}
uncles := make([]HexBytes, len(e.Uncles))
uncles := make([]hexutil.Bytes, len(e.Uncles))
for i, ucl := range e.Uncles {
uncles[i] = ucl
}
num, err := hexutil.DecodeBig(fmt.Sprintf("%#x", e.Number))
if err != nil {
return nil, err
}
diff, err := hexutil.DecodeBig(fmt.Sprintf("%#x", e.Difficulty))
if err != nil {
return nil, err
}
totalDiff, err := hexutil.DecodeBig(fmt.Sprintf("%#x", e.TotalDifficulty))
if err != nil {
return nil, err
}
size, err := hexutil.DecodeBig(fmt.Sprintf("%#x", e.Size))
if err != nil {
return nil, err
}
baseFee, err := hexutil.DecodeBig(fmt.Sprintf("%#x", e.BaseFeePerGas))
if err != nil {
return nil, err
}
return json.Marshal(executionBlockJSON{
Number: e.Number,
Number: num,
Hash: e.Hash,
ParentHash: e.ParentHash,
Sha3Uncles: e.Sha3Uncles,
@@ -108,30 +94,30 @@ func (e *ExecutionBlock) MarshalJSON() ([]byte, error) {
TransactionsRoot: e.TransactionsRoot,
ReceiptsRoot: e.ReceiptsRoot,
LogsBloom: e.LogsBloom,
Difficulty: e.Difficulty,
TotalDifficulty: e.TotalDifficulty,
GasLimit: Quantity(e.GasLimit),
GasUsed: Quantity(e.GasUsed),
Timestamp: Quantity(e.Timestamp),
Difficulty: diff,
TotalDifficulty: totalDiff,
GasLimit: hexutil.Uint64(e.GasLimit),
GasUsed: hexutil.Uint64(e.GasUsed),
Timestamp: hexutil.Uint64(e.Timestamp),
ExtraData: e.ExtraData,
MixHash: e.MixHash,
Nonce: e.Nonce,
Size: e.Size,
BaseFeePerGas: e.BaseFeePerGas,
Size: size,
BaseFeePerGas: baseFee,
Transactions: transactions,
Uncles: uncles,
})
}
// UnmarshalJSON defines a custom json.Unmarshaler interface implementation
// that uses custom json.Unmarshalers for the HexBytes and Quantity types.
// that uses custom json.Unmarshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
dec := executionBlockJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*e = ExecutionBlock{}
e.Number = dec.Number
e.Number = dec.Number.Bytes()
e.Hash = dec.Hash
e.ParentHash = dec.ParentHash
e.Sha3Uncles = dec.Sha3Uncles
@@ -140,16 +126,16 @@ func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
e.TransactionsRoot = dec.TransactionsRoot
e.ReceiptsRoot = dec.ReceiptsRoot
e.LogsBloom = dec.LogsBloom
e.Difficulty = dec.Difficulty
e.TotalDifficulty = dec.TotalDifficulty
e.Difficulty = dec.Difficulty.Bytes()
e.TotalDifficulty = dec.TotalDifficulty.Bytes()
e.GasLimit = uint64(dec.GasLimit)
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ExtraData = dec.ExtraData
e.MixHash = dec.MixHash
e.Nonce = dec.Nonce
e.Size = dec.Size
e.BaseFeePerGas = dec.BaseFeePerGas
e.Size = dec.Size.Bytes()
e.BaseFeePerGas = dec.BaseFeePerGas.Bytes()
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
transactions[i] = tx
@@ -164,28 +150,32 @@ func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
}
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"`
ParentHash hexutil.Bytes `json:"parentHash"`
FeeRecipient hexutil.Bytes `json:"feeRecipient"`
StateRoot hexutil.Bytes `json:"stateRoot"`
ReceiptsRoot hexutil.Bytes `json:"receiptsRoot"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
Random hexutil.Bytes `json:"random"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"`
BaseFeePerGas hexutil.Big `json:"baseFeePerGas"`
BlockHash hexutil.Bytes `json:"blockHash"`
Transactions []hexutil.Bytes `json:"transactions"`
}
// MarshalJSON --
func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
transactions := make([]HexBytes, len(e.Transactions))
transactions := make([]hexutil.Bytes, len(e.Transactions))
for i, tx := range e.Transactions {
transactions[i] = tx
}
var b hexutil.Big
if err := b.UnmarshalText(e.BaseFeePerGas); err != nil {
return nil, err
}
return json.Marshal(executionPayloadJSON{
ParentHash: e.ParentHash,
FeeRecipient: e.FeeRecipient,
@@ -193,12 +183,12 @@ func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
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),
BlockNumber: hexutil.Uint64(e.BlockNumber),
GasLimit: hexutil.Uint64(e.GasLimit),
GasUsed: hexutil.Uint64(e.GasUsed),
Timestamp: hexutil.Uint64(e.Timestamp),
ExtraData: e.ExtraData,
BaseFeePerGas: e.BaseFeePerGas,
BaseFeePerGas: b,
BlockHash: e.BlockHash,
Transactions: transactions,
})
@@ -208,6 +198,7 @@ func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
func (e *ExecutionPayload) UnmarshalJSON(enc []byte) error {
dec := executionPayloadJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
fmt.Println("got weird err")
return err
}
*e = ExecutionPayload{}
@@ -222,7 +213,11 @@ func (e *ExecutionPayload) UnmarshalJSON(enc []byte) error {
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ExtraData = dec.ExtraData
e.BaseFeePerGas = dec.BaseFeePerGas
baseFee, err := dec.BaseFeePerGas.MarshalText()
if err != nil {
return err
}
e.BaseFeePerGas = baseFee
e.BlockHash = dec.BlockHash
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
@@ -233,15 +228,15 @@ func (e *ExecutionPayload) UnmarshalJSON(enc []byte) error {
}
type payloadAttributesJSON struct {
Timestamp Quantity `json:"timestamp"`
Random HexBytes `json:"random"`
SuggestedFeeRecipient HexBytes `json:"suggestedFeeRecipient"`
Timestamp hexutil.Uint64 `json:"timestamp"`
Random hexutil.Bytes `json:"random"`
SuggestedFeeRecipient hexutil.Bytes `json:"suggestedFeeRecipient"`
}
// MarshalJSON --
func (p *PayloadAttributes) MarshalJSON() ([]byte, error) {
return json.Marshal(payloadAttributesJSON{
Timestamp: Quantity(p.Timestamp),
Timestamp: hexutil.Uint64(p.Timestamp),
Random: p.Random,
SuggestedFeeRecipient: p.SuggestedFeeRecipient,
})
@@ -261,9 +256,9 @@ func (p *PayloadAttributes) UnmarshalJSON(enc []byte) error {
}
type payloadStatusJSON struct {
LatestValidHash HexBytes `json:"latestValidHash"`
Status string `json:"status"`
ValidationError string `json:"validationError"`
LatestValidHash hexutil.Bytes `json:"latestValidHash"`
Status string `json:"status"`
ValidationError string `json:"validationError"`
}
// MarshalJSON --
@@ -289,9 +284,9 @@ func (p *PayloadStatus) UnmarshalJSON(enc []byte) error {
}
type forkchoiceStateJSON struct {
HeadBlockHash HexBytes `json:"headBlockHash"`
SafeBlockHash HexBytes `json:"safeBlockHash"`
FinalizedBlockHash HexBytes `json:"finalizedBlockHash"`
HeadBlockHash hexutil.Bytes `json:"headBlockHash"`
SafeBlockHash hexutil.Bytes `json:"safeBlockHash"`
FinalizedBlockHash hexutil.Bytes `json:"finalizedBlockHash"`
}
// MarshalJSON --

View File

@@ -2,7 +2,6 @@ package enginev1_test
import (
"encoding/json"
"math"
"testing"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
@@ -140,62 +139,13 @@ func TestJsonMarshalUnmarshal(t *testing.T) {
})
}
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)
})
}
func TestPayloadIDBytes_MarshalUnmarshalJSON(t *testing.T) {
item := [8]byte{1, 0, 0, 0, 0, 0, 0, 0}
enc, err := json.Marshal(enginev1.PayloadIDBytes(item))
require.NoError(t, err)
require.DeepEqual(t, "\"0x0100000000000000\"", string(enc))
res := &enginev1.PayloadIDBytes{}
err = res.UnmarshalJSON(enc)
require.NoError(t, err)
require.Equal(t, true, item == *res)
}