mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-12 22:15:07 -05:00
Compare commits
4 Commits
no-canonic
...
GetVersion
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
295dbaf85c | ||
|
|
fea9930c49 | ||
|
|
3b0f34a79f | ||
|
|
bb0f70ad60 |
@@ -63,6 +63,19 @@ type PeerCount struct {
|
||||
Connected string `json:"connected"`
|
||||
Disconnecting string `json:"disconnecting"`
|
||||
}
|
||||
type GetVersionV2Response struct {
|
||||
Data *VersionV2 `json:"data"`
|
||||
}
|
||||
type VersionV2 struct {
|
||||
BeaconNode *ClientVersionV1 `json:"beacon_node"`
|
||||
ExecutionClient *ClientVersionV1 `json:"execution_client,omitempty"`
|
||||
}
|
||||
type ClientVersionV1 struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
|
||||
type GetVersionResponse struct {
|
||||
Data *Version `json:"data"`
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"//testing/spectest:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositsnapshot:go_default_library",
|
||||
@@ -99,6 +100,7 @@ go_test(
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/execution/types"
|
||||
@@ -99,6 +100,8 @@ const (
|
||||
GetBlobsV1 = "engine_getBlobsV1"
|
||||
// GetBlobsV2 request string for JSON-RPC.
|
||||
GetBlobsV2 = "engine_getBlobsV2"
|
||||
// GetClientVersionV1 is the JSON-RPC method that identifies the execution client.
|
||||
GetClientVersionV1 = "engine_getClientVersionV1"
|
||||
// Defines the seconds before timing out engine endpoints with non-block execution semantics.
|
||||
defaultEngineTimeout = time.Second
|
||||
)
|
||||
@@ -135,6 +138,7 @@ type EngineCaller interface {
|
||||
GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (*blocks.GetPayloadResponse, error)
|
||||
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
|
||||
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
|
||||
GetClientVersionV1(ctx context.Context) ([]*structs.ClientVersionV1, error)
|
||||
}
|
||||
|
||||
var ErrEmptyBlockHash = errors.New("Block hash is empty 0x0000...")
|
||||
@@ -553,6 +557,39 @@ func (s *Service) GetBlobsV2(ctx context.Context, versionedHashes []common.Hash)
|
||||
return result, handleRPCError(err)
|
||||
}
|
||||
|
||||
func (s *Service) GetClientVersionV1(ctx context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetClientVersionV1")
|
||||
defer span.End()
|
||||
|
||||
commit := version.GitCommit()
|
||||
if len(commit) >= 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
|
||||
var result []*structs.ClientVersionV1
|
||||
err := s.rpcClient.CallContext(
|
||||
ctx,
|
||||
&result,
|
||||
GetClientVersionV1,
|
||||
structs.ClientVersionV1{
|
||||
Code: "PM",
|
||||
Name: "Prysm",
|
||||
Version: version.SemanticVersion(),
|
||||
Commit: commit,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, handleRPCError(err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, errors.New("execution client returned no result")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReconstructFullBlock takes in a blinded beacon block and reconstructs
|
||||
// a beacon block with a full execution payload via the engine API.
|
||||
func (s *Service) ReconstructFullBlock(
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/filesystem"
|
||||
@@ -999,6 +1000,99 @@ func TestClient_HTTP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, want, resp)
|
||||
})
|
||||
t.Run(GetClientVersionV1, func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want any
|
||||
resp []*structs.ClientVersionV1
|
||||
hasError bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
want: []*structs.ClientVersionV1{{
|
||||
Code: "GE",
|
||||
Name: "go-ethereum",
|
||||
Version: "1.15.11-stable",
|
||||
Commit: "36b2371c",
|
||||
}},
|
||||
resp: []*structs.ClientVersionV1{{
|
||||
Code: "GE",
|
||||
Name: "go-ethereum",
|
||||
Version: "1.15.11-stable",
|
||||
Commit: "36b2371c",
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "empty response",
|
||||
want: []*structs.ClientVersionV1{},
|
||||
hasError: true,
|
||||
errMsg: "execution client returned no result",
|
||||
},
|
||||
{
|
||||
name: "RPC error",
|
||||
want: "brokenMsg",
|
||||
hasError: true,
|
||||
errMsg: "unexpected error in JSON-RPC",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
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 := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
jsonRequestString := string(enc)
|
||||
|
||||
// We expect the JSON string RPC request contains the right method name.
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, GetClientVersionV1,
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, "\"code\":\"PM\"",
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, "\"name\":\"Prysm\"",
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, fmt.Sprintf("\"version\":\"%s\"", version.SemanticVersion()),
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, fmt.Sprintf("\"commit\":\"%s\"", version.GitCommit()[:8]),
|
||||
))
|
||||
resp := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": tc.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()
|
||||
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
// We call the RPC method via HTTP and expect a proper result.
|
||||
resp, err := service.GetClientVersionV1(ctx)
|
||||
if tc.hasError {
|
||||
require.NotNil(t, err)
|
||||
require.ErrorContains(t, tc.errMsg, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, tc.resp, resp)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestReconstructFullBellatrixBlock(t *testing.T) {
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/core/peerdas:go_default_library",
|
||||
"//beacon-chain/execution/types:go_default_library",
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
@@ -42,6 +43,8 @@ type EngineClient struct {
|
||||
ErrorBlobSidecars error
|
||||
DataColumnSidecars []blocks.VerifiedRODataColumn
|
||||
ErrorDataColumnSidecars error
|
||||
ClientVersion []*structs.ClientVersionV1
|
||||
ErrorClientVersion error
|
||||
}
|
||||
|
||||
// NewPayload --
|
||||
@@ -173,3 +176,8 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context, transitionTime
|
||||
blk = parentBlk
|
||||
}
|
||||
}
|
||||
|
||||
// GetClientVersionV1 --
|
||||
func (e *EngineClient) GetClientVersionV1(context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
return e.ClientVersion, e.ErrorClientVersion
|
||||
}
|
||||
|
||||
@@ -405,6 +405,7 @@ func (s *Service) nodeEndpoints() []endpoint {
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
|
||||
}
|
||||
|
||||
const namespace = "node"
|
||||
@@ -469,6 +470,16 @@ func (s *Service) nodeEndpoints() []endpoint {
|
||||
handler: server.GetVersion,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/node/version",
|
||||
name: namespace + ".GetVersionV2",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.GetVersionV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/health",
|
||||
name: namespace + ".GetHealth",
|
||||
|
||||
@@ -86,6 +86,7 @@ func Test_endpoints(t *testing.T) {
|
||||
"/eth/v1/node/peers/{peer_id}": {http.MethodGet},
|
||||
"/eth/v1/node/peer_count": {http.MethodGet},
|
||||
"/eth/v1/node/version": {http.MethodGet},
|
||||
"/eth/v2/node/version": {http.MethodGet},
|
||||
"/eth/v1/node/syncing": {http.MethodGet},
|
||||
"/eth/v1/node/health": {http.MethodGet},
|
||||
}
|
||||
|
||||
@@ -163,11 +163,7 @@ func (s *Server) GetBlockV2(w http.ResponseWriter, r *http.Request) {
|
||||
if blk.Version() >= version.Bellatrix && blk.IsBlinded() {
|
||||
blk, err = s.ExecutionReconstructor.ReconstructFullBlock(ctx, blk)
|
||||
if err != nil {
|
||||
if errors.Is(err, blocks.ErrNonCanonicalBlock) {
|
||||
httputil.HandleError(w, fmt.Sprintf("no canonical block found for block %s: execution payload is unavailable (block may have been orphaned)", blockId), http.StatusNotFound)
|
||||
} else {
|
||||
httputil.HandleError(w, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block").Error(), http.StatusInternalServerError)
|
||||
}
|
||||
httputil.HandleError(w, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block").Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"handlers.go",
|
||||
"handlers_peers.go",
|
||||
"log.go",
|
||||
"server.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/node",
|
||||
@@ -30,6 +31,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//core/peer:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -44,6 +46,7 @@ go_test(
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/execution/testing:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/p2p/peers:go_default_library",
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
|
||||
@@ -103,6 +103,8 @@ func (s *Server) GetIdentity(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// GetVersion requests that the beacon node identify information about its implementation in a
|
||||
// format similar to a HTTP User-Agent field.
|
||||
//
|
||||
// Deprecated: in favour of GetVersionV2.
|
||||
func (*Server) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "node.GetVersion")
|
||||
defer span.End()
|
||||
@@ -116,6 +118,38 @@ func (*Server) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.WriteJson(w, resp)
|
||||
}
|
||||
|
||||
// GetVersionV2 Retrieves structured information about the version of the beacon node and its attached
|
||||
// execution client in the same format as used on the Engine API
|
||||
func (s *Server) GetVersionV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "node.GetVersionV2")
|
||||
defer span.End()
|
||||
|
||||
var elData *structs.ClientVersionV1
|
||||
elDataList, err := s.ExecutionEngineCaller.GetClientVersionV1(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("endpoint", "GetVersionV2").Debug("Could not get execution client version")
|
||||
} else if len(elDataList) > 0 {
|
||||
elData = elDataList[0]
|
||||
}
|
||||
|
||||
commit := version.GitCommit()
|
||||
if len(commit) >= 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
resp := &structs.GetVersionV2Response{
|
||||
Data: &structs.VersionV2{
|
||||
BeaconNode: &structs.ClientVersionV1{
|
||||
Code: "PM",
|
||||
Name: "Prysm",
|
||||
Version: version.SemanticVersion(),
|
||||
Commit: commit,
|
||||
},
|
||||
ExecutionClient: elData,
|
||||
},
|
||||
}
|
||||
httputil.WriteJson(w, resp)
|
||||
}
|
||||
|
||||
// GetHealth returns node health status in http status codes. Useful for load balancers.
|
||||
func (s *Server) GetHealth(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "node.GetHealth")
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/OffchainLabs/go-bitfield"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
mockengine "github.com/OffchainLabs/prysm/v7/beacon-chain/execution/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
mockp2p "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/testutil"
|
||||
@@ -90,6 +91,75 @@ func TestGetVersion(t *testing.T) {
|
||||
assert.StringContains(t, arch, resp.Data.Version)
|
||||
}
|
||||
|
||||
func TestGetVersionV2(t *testing.T) {
|
||||
t.Run("happy path", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/node/version", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s := &Server{
|
||||
ExecutionEngineCaller: &mockengine.EngineClient{
|
||||
ClientVersion: []*structs.ClientVersionV1{{
|
||||
Code: "EL",
|
||||
Name: "ExecutionClient",
|
||||
Version: "v1.0.0",
|
||||
Commit: "abcdef12",
|
||||
}},
|
||||
},
|
||||
}
|
||||
s.GetVersionV2(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetVersionV2Response{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.NotNil(t, resp)
|
||||
require.NotNil(t, resp.Data)
|
||||
require.NotNil(t, resp.Data.BeaconNode)
|
||||
require.NotNil(t, resp.Data.ExecutionClient)
|
||||
require.Equal(t, "EL", resp.Data.ExecutionClient.Code)
|
||||
require.Equal(t, "ExecutionClient", resp.Data.ExecutionClient.Name)
|
||||
require.Equal(t, "v1.0.0", resp.Data.ExecutionClient.Version)
|
||||
require.Equal(t, "abcdef12", resp.Data.ExecutionClient.Commit)
|
||||
require.Equal(t, "PM", resp.Data.BeaconNode.Code)
|
||||
require.Equal(t, "Prysm", resp.Data.BeaconNode.Name)
|
||||
require.Equal(t, version.SemanticVersion(), resp.Data.BeaconNode.Version)
|
||||
require.Equal(t, true, len(resp.Data.BeaconNode.Commit) <= 8)
|
||||
})
|
||||
|
||||
t.Run("unhappy path", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/node/version", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s := &Server{
|
||||
ExecutionEngineCaller: &mockengine.EngineClient{
|
||||
ClientVersion: nil,
|
||||
ErrorClientVersion: fmt.Errorf("error"),
|
||||
},
|
||||
}
|
||||
s.GetVersionV2(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetVersionV2Response{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.NotNil(t, resp)
|
||||
require.NotNil(t, resp.Data)
|
||||
require.NotNil(t, resp.Data.BeaconNode)
|
||||
require.Equal(t, true, resp.Data.ExecutionClient == nil)
|
||||
|
||||
// make sure there is no 'execution_client' field
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &payload))
|
||||
data, ok := payload["data"].(map[string]any)
|
||||
require.Equal(t, true, ok)
|
||||
_, found := data["beacon_node"]
|
||||
require.Equal(t, true, found)
|
||||
_, found = data["execution_client"]
|
||||
require.Equal(t, false, found)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGetHealth(t *testing.T) {
|
||||
checker := &syncmock.Sync{}
|
||||
optimisticFetcher := &mock.ChainService{Optimistic: false}
|
||||
|
||||
9
beacon-chain/rpc/eth/node/log.go
Normal file
9
beacon-chain/rpc/eth/node/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Code generated by hack/gen-logs.sh; DO NOT EDIT.
|
||||
// This file is created and regenerated automatically. Anything added here might get removed.
|
||||
package node
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
// The prefix for logs from this package will be the text after the last slash in the package path.
|
||||
// If you wish to change this, you should add your desired name in the runtime/logging/logrus-prefixed-formatter/prefix-replacement.go file.
|
||||
var log = logrus.WithField("package", "beacon-chain/rpc/eth/node")
|
||||
@@ -26,4 +26,5 @@ type Server struct {
|
||||
GenesisTimeFetcher blockchain.TimeFetcher
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
ExecutionChainInfoFetcher execution.ChainInfoFetcher
|
||||
ExecutionEngineCaller execution.EngineCaller
|
||||
}
|
||||
|
||||
3
changelog/bastin_get-version-v2.md
Normal file
3
changelog/bastin_get-version-v2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- New beacon API endpoint `eth/v2/node/version`.
|
||||
3
changelog/tt_ro-payload-envelope.md
Normal file
3
changelog/tt_ro-payload-envelope.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Add read only wrapper for execution payload envelope for gloas
|
||||
@@ -4,6 +4,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"execution.go",
|
||||
"execution_payload_envelope.go",
|
||||
"factory.go",
|
||||
"get_payload.go",
|
||||
"getters.go",
|
||||
@@ -45,6 +46,7 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"execution_payload_envelope_test.go",
|
||||
"execution_test.go",
|
||||
"factory_test.go",
|
||||
"getters_test.go",
|
||||
|
||||
131
consensus-types/blocks/execution_payload_envelope.go
Normal file
131
consensus-types/blocks/execution_payload_envelope.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package blocks
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
field_params "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type signedExecutionPayloadEnvelope struct {
|
||||
s *ethpb.SignedExecutionPayloadEnvelope
|
||||
}
|
||||
|
||||
type executionPayloadEnvelope struct {
|
||||
p *ethpb.ExecutionPayloadEnvelope
|
||||
}
|
||||
|
||||
// WrappedROSignedExecutionPayloadEnvelope wraps a signed execution payload envelope proto in a read-only interface.
|
||||
func WrappedROSignedExecutionPayloadEnvelope(s *ethpb.SignedExecutionPayloadEnvelope) (interfaces.ROSignedExecutionPayloadEnvelope, error) {
|
||||
w := signedExecutionPayloadEnvelope{s: s}
|
||||
if w.IsNil() {
|
||||
return nil, consensus_types.ErrNilObjectWrapped
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// WrappedROExecutionPayloadEnvelope wraps an execution payload envelope proto in a read-only interface.
|
||||
func WrappedROExecutionPayloadEnvelope(p *ethpb.ExecutionPayloadEnvelope) (interfaces.ROExecutionPayloadEnvelope, error) {
|
||||
w := &executionPayloadEnvelope{p: p}
|
||||
if w.IsNil() {
|
||||
return nil, consensus_types.ErrNilObjectWrapped
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Envelope returns the execution payload envelope as a read-only interface.
|
||||
func (s signedExecutionPayloadEnvelope) Envelope() (interfaces.ROExecutionPayloadEnvelope, error) {
|
||||
return WrappedROExecutionPayloadEnvelope(s.s.Message)
|
||||
}
|
||||
|
||||
// Signature returns the BLS signature as a 96-byte array.
|
||||
func (s signedExecutionPayloadEnvelope) Signature() [field_params.BLSSignatureLength]byte {
|
||||
return [field_params.BLSSignatureLength]byte(s.s.Signature)
|
||||
}
|
||||
|
||||
// IsNil reports whether the signed envelope or its contents are invalid.
|
||||
func (s signedExecutionPayloadEnvelope) IsNil() bool {
|
||||
if s.s == nil {
|
||||
return true
|
||||
}
|
||||
if len(s.s.Signature) != field_params.BLSSignatureLength {
|
||||
return true
|
||||
}
|
||||
if len(s.s.Message.BeaconBlockRoot) != field_params.RootLength {
|
||||
return true
|
||||
}
|
||||
if len(s.s.Message.StateRoot) != field_params.RootLength {
|
||||
return true
|
||||
}
|
||||
if s.s.Message.ExecutionRequests == nil {
|
||||
return true
|
||||
}
|
||||
if s.s.Message.Payload == nil {
|
||||
return true
|
||||
}
|
||||
w := executionPayloadEnvelope{p: s.s.Message}
|
||||
return w.IsNil()
|
||||
}
|
||||
|
||||
// SigningRoot computes the signing root for the signed envelope with the provided domain.
|
||||
func (s signedExecutionPayloadEnvelope) SigningRoot(domain []byte) (root [32]byte, err error) {
|
||||
return signing.ComputeSigningRoot(s.s.Message, domain)
|
||||
}
|
||||
|
||||
// Proto returns the underlying protobuf message.
|
||||
func (s signedExecutionPayloadEnvelope) Proto() proto.Message {
|
||||
return s.s
|
||||
}
|
||||
|
||||
// IsNil reports whether the envelope or its required fields are invalid.
|
||||
func (p *executionPayloadEnvelope) IsNil() bool {
|
||||
if p.p == nil {
|
||||
return true
|
||||
}
|
||||
if p.p.Payload == nil {
|
||||
return true
|
||||
}
|
||||
if len(p.p.BeaconBlockRoot) != field_params.RootLength {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsBlinded reports whether the envelope contains a blinded payload.
|
||||
func (p *executionPayloadEnvelope) IsBlinded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Execution returns the execution payload as a read-only interface.
|
||||
func (p *executionPayloadEnvelope) Execution() (interfaces.ExecutionData, error) {
|
||||
return WrappedExecutionPayloadDeneb(p.p.Payload)
|
||||
}
|
||||
|
||||
// ExecutionRequests returns the execution requests attached to the envelope.
|
||||
func (p *executionPayloadEnvelope) ExecutionRequests() *enginev1.ExecutionRequests {
|
||||
return ethpb.CopyExecutionRequests(p.p.ExecutionRequests)
|
||||
}
|
||||
|
||||
// BuilderIndex returns the proposer/builder index for the envelope.
|
||||
func (p *executionPayloadEnvelope) BuilderIndex() primitives.BuilderIndex {
|
||||
return p.p.BuilderIndex
|
||||
}
|
||||
|
||||
// BeaconBlockRoot returns the beacon block root referenced by the envelope.
|
||||
func (p *executionPayloadEnvelope) BeaconBlockRoot() [field_params.RootLength]byte {
|
||||
return [field_params.RootLength]byte(p.p.BeaconBlockRoot)
|
||||
}
|
||||
|
||||
// Slot returns the slot of the envelope.
|
||||
func (p *executionPayloadEnvelope) Slot() primitives.Slot {
|
||||
return p.p.Slot
|
||||
}
|
||||
|
||||
// StateRoot returns the state root carried by the envelope.
|
||||
func (p *executionPayloadEnvelope) StateRoot() [field_params.RootLength]byte {
|
||||
return [field_params.RootLength]byte(p.p.StateRoot)
|
||||
}
|
||||
136
consensus-types/blocks/execution_payload_envelope_test.go
Normal file
136
consensus-types/blocks/execution_payload_envelope_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package blocks_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func validExecutionPayloadEnvelope() *ethpb.ExecutionPayloadEnvelope {
|
||||
payload := &enginev1.ExecutionPayloadDeneb{
|
||||
ParentHash: bytes.Repeat([]byte{0x01}, 32),
|
||||
FeeRecipient: bytes.Repeat([]byte{0x02}, 20),
|
||||
StateRoot: bytes.Repeat([]byte{0x03}, 32),
|
||||
ReceiptsRoot: bytes.Repeat([]byte{0x04}, 32),
|
||||
LogsBloom: bytes.Repeat([]byte{0x05}, 256),
|
||||
PrevRandao: bytes.Repeat([]byte{0x06}, 32),
|
||||
BlockNumber: 1,
|
||||
GasLimit: 2,
|
||||
GasUsed: 3,
|
||||
Timestamp: 4,
|
||||
BaseFeePerGas: bytes.Repeat([]byte{0x07}, 32),
|
||||
BlockHash: bytes.Repeat([]byte{0x08}, 32),
|
||||
Transactions: [][]byte{},
|
||||
Withdrawals: []*enginev1.Withdrawal{},
|
||||
BlobGasUsed: 0,
|
||||
ExcessBlobGas: 0,
|
||||
}
|
||||
|
||||
return ðpb.ExecutionPayloadEnvelope{
|
||||
Payload: payload,
|
||||
ExecutionRequests: &enginev1.ExecutionRequests{
|
||||
Deposits: []*enginev1.DepositRequest{
|
||||
{
|
||||
Pubkey: bytes.Repeat([]byte{0x09}, 48),
|
||||
WithdrawalCredentials: bytes.Repeat([]byte{0x0A}, 32),
|
||||
Signature: bytes.Repeat([]byte{0x0B}, 96),
|
||||
},
|
||||
},
|
||||
},
|
||||
BuilderIndex: 10,
|
||||
BeaconBlockRoot: bytes.Repeat([]byte{0xAA}, 32),
|
||||
Slot: 9,
|
||||
StateRoot: bytes.Repeat([]byte{0xBB}, 32),
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrappedROExecutionPayloadEnvelope(t *testing.T) {
|
||||
t.Run("returns error on nil payload", func(t *testing.T) {
|
||||
invalid := validExecutionPayloadEnvelope()
|
||||
invalid.Payload = nil
|
||||
_, err := blocks.WrappedROExecutionPayloadEnvelope(invalid)
|
||||
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
|
||||
})
|
||||
|
||||
t.Run("returns error on invalid beacon root length", func(t *testing.T) {
|
||||
invalid := validExecutionPayloadEnvelope()
|
||||
invalid.BeaconBlockRoot = []byte{0x01}
|
||||
_, err := blocks.WrappedROExecutionPayloadEnvelope(invalid)
|
||||
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
|
||||
})
|
||||
|
||||
t.Run("wraps and exposes fields", func(t *testing.T) {
|
||||
env := validExecutionPayloadEnvelope()
|
||||
wrapped, err := blocks.WrappedROExecutionPayloadEnvelope(env)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, primitives.BuilderIndex(10), wrapped.BuilderIndex())
|
||||
require.Equal(t, primitives.Slot(9), wrapped.Slot())
|
||||
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xAA}, 32)), wrapped.BeaconBlockRoot())
|
||||
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xBB}, 32)), wrapped.StateRoot())
|
||||
|
||||
reqs := wrapped.ExecutionRequests()
|
||||
require.NotNil(t, reqs)
|
||||
if len(reqs.Deposits) > 0 {
|
||||
reqs.Deposits[0].Pubkey[0] = 0xFF
|
||||
require.NotEqual(t, reqs.Deposits[0].Pubkey[0], env.ExecutionRequests.Deposits[0].Pubkey[0])
|
||||
}
|
||||
|
||||
exec, err := wrapped.Execution()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, env.Payload.ParentHash, exec.ParentHash())
|
||||
|
||||
require.Equal(t, false, wrapped.IsBlinded())
|
||||
})
|
||||
}
|
||||
|
||||
func TestWrappedROSignedExecutionPayloadEnvelope(t *testing.T) {
|
||||
t.Run("returns error for invalid signature length", func(t *testing.T) {
|
||||
signed := ðpb.SignedExecutionPayloadEnvelope{
|
||||
Message: validExecutionPayloadEnvelope(),
|
||||
Signature: bytes.Repeat([]byte{0xAA}, 95),
|
||||
}
|
||||
_, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signed)
|
||||
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
|
||||
})
|
||||
|
||||
t.Run("returns error on nil envelope", func(t *testing.T) {
|
||||
_, err := blocks.WrappedROSignedExecutionPayloadEnvelope(nil)
|
||||
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
|
||||
})
|
||||
|
||||
t.Run("wraps and provides envelope/signing data", func(t *testing.T) {
|
||||
sig := bytes.Repeat([]byte{0xAB}, 96)
|
||||
signed := ðpb.SignedExecutionPayloadEnvelope{
|
||||
Message: validExecutionPayloadEnvelope(),
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
wrapped, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signed)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotSig := wrapped.Signature()
|
||||
assert.DeepEqual(t, [96]byte(sig), gotSig)
|
||||
|
||||
env, err := wrapped.Envelope()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xAA}, 32)), env.BeaconBlockRoot())
|
||||
|
||||
domain := bytes.Repeat([]byte{0xCC}, 32)
|
||||
wantRoot, err := signing.ComputeSigningRoot(signed.Message, domain)
|
||||
require.NoError(t, err)
|
||||
gotRoot, err := wrapped.SigningRoot(domain)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantRoot, gotRoot)
|
||||
|
||||
require.Equal(t, signed, wrapped.Proto())
|
||||
})
|
||||
}
|
||||
@@ -27,11 +27,6 @@ var (
|
||||
// ErrNilBeaconBlock is returned when a nil beacon block is received.
|
||||
ErrNilBeaconBlock = errors.New("beacon block can't be nil")
|
||||
errNonBlindedSignedBeaconBlock = errors.New("can only build signed beacon block from blinded format")
|
||||
// ErrNonCanonicalBlock is returned when a reconstructed execution payload does
|
||||
// not match the expected payload header. This occurs when the execution layer
|
||||
// returns the canonical block's payload at the same height instead of the
|
||||
// requested (orphaned/reorged) block's payload.
|
||||
ErrNonCanonicalBlock = errors.New("no canonical block found for payload header")
|
||||
)
|
||||
|
||||
// NewSignedBeaconBlock creates a signed beacon block from a protobuf signed beacon block.
|
||||
@@ -313,11 +308,11 @@ func checkPayloadAgainstHeader(wrappedPayload, payloadHeader interfaces.Executio
|
||||
return errors.Wrap(err, "could not hash tree root payload header")
|
||||
}
|
||||
if payloadRoot != payloadHeaderRoot {
|
||||
return errors.Wrap(ErrNonCanonicalBlock, fmt.Sprintf(
|
||||
return fmt.Errorf(
|
||||
"payload %#x and header %#x roots do not match",
|
||||
payloadRoot,
|
||||
payloadHeaderRoot,
|
||||
))
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"beacon_block.go",
|
||||
"error.go",
|
||||
"execution_payload_envelope.go",
|
||||
"light_client.go",
|
||||
"signed_execution_payload_bid.go",
|
||||
"utils.go",
|
||||
|
||||
27
consensus-types/interfaces/execution_payload_envelope.go
Normal file
27
consensus-types/interfaces/execution_payload_envelope.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
field_params "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ROSignedExecutionPayloadEnvelope interface {
|
||||
Envelope() (ROExecutionPayloadEnvelope, error)
|
||||
Signature() [field_params.BLSSignatureLength]byte
|
||||
SigningRoot([]byte) ([32]byte, error)
|
||||
IsNil() bool
|
||||
Proto() proto.Message
|
||||
}
|
||||
|
||||
type ROExecutionPayloadEnvelope interface {
|
||||
Execution() (ExecutionData, error)
|
||||
ExecutionRequests() *enginev1.ExecutionRequests
|
||||
BuilderIndex() primitives.BuilderIndex
|
||||
BeaconBlockRoot() [field_params.RootLength]byte
|
||||
Slot() primitives.Slot
|
||||
StateRoot() [field_params.RootLength]byte
|
||||
IsBlinded() bool
|
||||
IsNil() bool
|
||||
}
|
||||
@@ -34,8 +34,17 @@ func SemanticVersion() string {
|
||||
return gitTag
|
||||
}
|
||||
|
||||
// GitCommit returns the current build commit hash.
|
||||
func GitCommit() string {
|
||||
return resolvedGitCommit()
|
||||
}
|
||||
|
||||
// BuildData returns the git tag and commit of the current build.
|
||||
func BuildData() string {
|
||||
return fmt.Sprintf("Prysm/%s/%s", gitTag, resolvedGitCommit())
|
||||
}
|
||||
|
||||
func resolvedGitCommit() string {
|
||||
// if doing a local build, these values are not interpolated
|
||||
if gitCommit == "{STABLE_GIT_COMMIT}" {
|
||||
commit, err := exec.Command("git", "rev-parse", "HEAD").Output()
|
||||
@@ -45,5 +54,5 @@ func BuildData() string {
|
||||
gitCommit = strings.TrimRight(string(commit), "\r\n")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("Prysm/%s/%s", gitTag, gitCommit)
|
||||
return gitCommit
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ go_library(
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/testing/spectest/shared/common/forkchoice",
|
||||
visibility = ["//testing/spectest:__subpackages__"],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
@@ -141,3 +142,7 @@ func (m *engineMock) ExecutionBlockByHash(_ context.Context, hash common.Hash, _
|
||||
func (m *engineMock) GetTerminalBlockHash(context.Context, uint64) ([]byte, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (m *engineMock) GetClientVersionV1(context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user