mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-06 20:13:59 -05:00
Builder: Electra (#14344)
* removing skip from test * builder wip * removing todo, it's probably ok * adding more TODOs * adding fromProtoElectra * using lightclient s branch and updating values * minor fixes * rolling back dependency changes * go mod tidy * adding space back in * updating builder changes based on execution request changes * update ssz * changelog * updating based on execution request changes * fixing validation * adding builder test for electra * gaz * attempting to fix test * fixing ssz * fixing build and handling develop changes * gaz * fixing unfinished function * fixing test * fixing important missed regression * removing unneeded validations * missed linting * gofmt * fixing fulu test * fixing changelog * Update bid.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update bid.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update types.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update types.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update james-prysm_builder-electra.md Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update testing/middleware/builder/builder.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * addressing review feedback and updating e2e * fixing parsing bid version * reversing incorrect check * improving tests and updating more code based on review feedback * gofmt * fixing unit tests * more feedback from terence * gofmt * Update api/client/builder/types.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update api/client/builder/types.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * addressing nitpicks * gofmt * radek feedback * improves error --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -13,6 +13,7 @@ go_library(
|
||||
deps = [
|
||||
"//api:go_default_library",
|
||||
"//api/client:go_default_library",
|
||||
"//api/server:go_default_library",
|
||||
"//api/server/structs:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
@@ -27,6 +28,7 @@ go_library(
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_fastssz//:go_default_library",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
ssz "github.com/prysmaticlabs/fastssz"
|
||||
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
)
|
||||
@@ -22,7 +22,6 @@ type SignedBid interface {
|
||||
// Bid is an interface describing the method set of a builder bid.
|
||||
type Bid interface {
|
||||
Header() (interfaces.ExecutionData, error)
|
||||
BlobKzgCommitments() ([][]byte, error)
|
||||
Value() primitives.Wei
|
||||
Pubkey() []byte
|
||||
Version() int
|
||||
@@ -31,6 +30,18 @@ type Bid interface {
|
||||
HashTreeRootWith(hh *ssz.Hasher) error
|
||||
}
|
||||
|
||||
// BidDeneb is an interface that exposes newly added kzg commitments on top of builder bid
|
||||
type BidDeneb interface {
|
||||
Bid
|
||||
BlobKzgCommitments() [][]byte
|
||||
}
|
||||
|
||||
// BidElectra is an interface that exposes the newly added execution requests on top of the builder bid
|
||||
type BidElectra interface {
|
||||
BidDeneb
|
||||
ExecutionRequests() *v1.ExecutionRequests
|
||||
}
|
||||
|
||||
type signedBuilderBid struct {
|
||||
p *ethpb.SignedBuilderBid
|
||||
}
|
||||
@@ -115,11 +126,6 @@ func (b builderBid) Header() (interfaces.ExecutionData, error) {
|
||||
return blocks.WrappedExecutionPayloadHeader(b.p.Header)
|
||||
}
|
||||
|
||||
// BlobKzgCommitments --
|
||||
func (b builderBid) BlobKzgCommitments() ([][]byte, error) {
|
||||
return [][]byte{}, errors.New("blob kzg commitments not available before Deneb")
|
||||
}
|
||||
|
||||
// Version --
|
||||
func (b builderBid) Version() int {
|
||||
return version.Bellatrix
|
||||
@@ -169,11 +175,6 @@ func (b builderBidCapella) Header() (interfaces.ExecutionData, error) {
|
||||
return blocks.WrappedExecutionPayloadHeaderCapella(b.p.Header)
|
||||
}
|
||||
|
||||
// BlobKzgCommitments --
|
||||
func (b builderBidCapella) BlobKzgCommitments() ([][]byte, error) {
|
||||
return [][]byte{}, errors.New("blob kzg commitments not available before Deneb")
|
||||
}
|
||||
|
||||
// Version --
|
||||
func (b builderBidCapella) Version() int {
|
||||
return version.Capella
|
||||
@@ -254,8 +255,8 @@ func (b builderBidDeneb) Header() (interfaces.ExecutionData, error) {
|
||||
}
|
||||
|
||||
// BlobKzgCommitments --
|
||||
func (b builderBidDeneb) BlobKzgCommitments() ([][]byte, error) {
|
||||
return b.p.BlobKzgCommitments, nil
|
||||
func (b builderBidDeneb) BlobKzgCommitments() [][]byte {
|
||||
return b.p.BlobKzgCommitments
|
||||
}
|
||||
|
||||
type signedBuilderBidDeneb struct {
|
||||
@@ -290,3 +291,95 @@ func (b signedBuilderBidDeneb) Version() int {
|
||||
func (b signedBuilderBidDeneb) IsNil() bool {
|
||||
return b.p == nil
|
||||
}
|
||||
|
||||
type builderBidElectra struct {
|
||||
p *ethpb.BuilderBidElectra
|
||||
}
|
||||
|
||||
// WrappedBuilderBidElectra is a constructor which wraps a protobuf bid into an interface.
|
||||
func WrappedBuilderBidElectra(p *ethpb.BuilderBidElectra) (Bid, error) {
|
||||
w := builderBidElectra{p: p}
|
||||
if w.IsNil() {
|
||||
return nil, consensus_types.ErrNilObjectWrapped
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Version --
|
||||
func (b builderBidElectra) Version() int {
|
||||
return version.Electra
|
||||
}
|
||||
|
||||
// Value --
|
||||
func (b builderBidElectra) Value() primitives.Wei {
|
||||
return primitives.LittleEndianBytesToWei(b.p.Value)
|
||||
}
|
||||
|
||||
// Pubkey --
|
||||
func (b builderBidElectra) Pubkey() []byte {
|
||||
return b.p.Pubkey
|
||||
}
|
||||
|
||||
// IsNil --
|
||||
func (b builderBidElectra) IsNil() bool {
|
||||
return b.p == nil
|
||||
}
|
||||
|
||||
// HashTreeRoot --
|
||||
func (b builderBidElectra) HashTreeRoot() ([32]byte, error) {
|
||||
return b.p.HashTreeRoot()
|
||||
}
|
||||
|
||||
// HashTreeRootWith --
|
||||
func (b builderBidElectra) HashTreeRootWith(hh *ssz.Hasher) error {
|
||||
return b.p.HashTreeRootWith(hh)
|
||||
}
|
||||
|
||||
// Header --
|
||||
func (b builderBidElectra) Header() (interfaces.ExecutionData, error) {
|
||||
// We have to convert big endian to little endian because the value is coming from the execution layer.
|
||||
return blocks.WrappedExecutionPayloadHeaderDeneb(b.p.Header)
|
||||
}
|
||||
|
||||
// ExecutionRequests --
|
||||
func (b builderBidElectra) ExecutionRequests() *v1.ExecutionRequests {
|
||||
return b.p.ExecutionRequests // does not copy
|
||||
}
|
||||
|
||||
// BlobKzgCommitments --
|
||||
func (b builderBidElectra) BlobKzgCommitments() [][]byte {
|
||||
return b.p.BlobKzgCommitments
|
||||
}
|
||||
|
||||
type signedBuilderBidElectra struct {
|
||||
p *ethpb.SignedBuilderBidElectra
|
||||
}
|
||||
|
||||
// WrappedSignedBuilderBidElectra is a constructor which wraps a protobuf signed bit into an interface.
|
||||
func WrappedSignedBuilderBidElectra(p *ethpb.SignedBuilderBidElectra) (SignedBid, error) {
|
||||
w := signedBuilderBidElectra{p: p}
|
||||
if w.IsNil() {
|
||||
return nil, consensus_types.ErrNilObjectWrapped
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Message --
|
||||
func (b signedBuilderBidElectra) Message() (Bid, error) {
|
||||
return WrappedBuilderBidElectra(b.p.Message)
|
||||
}
|
||||
|
||||
// Signature --
|
||||
func (b signedBuilderBidElectra) Signature() []byte {
|
||||
return b.p.Signature
|
||||
}
|
||||
|
||||
// Version --
|
||||
func (b signedBuilderBidElectra) Version() int {
|
||||
return version.Electra
|
||||
}
|
||||
|
||||
// IsNil --
|
||||
func (b signedBuilderBidElectra) IsNil() bool {
|
||||
return b.p == nil
|
||||
}
|
||||
|
||||
@@ -219,8 +219,23 @@ func (c *Client) GetHeader(ctx context.Context, slot primitives.Slot, parentHash
|
||||
if err := json.Unmarshal(hb, v); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
|
||||
}
|
||||
switch strings.ToLower(v.Version) {
|
||||
case strings.ToLower(version.String(version.Deneb)):
|
||||
|
||||
ver, err := version.FromString(strings.ToLower(v.Version))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("unsupported header version %s", strings.ToLower(v.Version)))
|
||||
}
|
||||
if ver >= version.Electra {
|
||||
hr := &ExecHeaderResponseElectra{}
|
||||
if err := json.Unmarshal(hb, hr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
|
||||
}
|
||||
p, err := hr.ToProto()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not extract proto message from header")
|
||||
}
|
||||
return WrappedSignedBuilderBidElectra(p)
|
||||
}
|
||||
if ver >= version.Deneb {
|
||||
hr := &ExecHeaderResponseDeneb{}
|
||||
if err := json.Unmarshal(hb, hr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
|
||||
@@ -230,7 +245,8 @@ func (c *Client) GetHeader(ctx context.Context, slot primitives.Slot, parentHash
|
||||
return nil, errors.Wrapf(err, "could not extract proto message from header")
|
||||
}
|
||||
return WrappedSignedBuilderBidDeneb(p)
|
||||
case strings.ToLower(version.String(version.Capella)):
|
||||
}
|
||||
if ver >= version.Capella {
|
||||
hr := &ExecHeaderResponseCapella{}
|
||||
if err := json.Unmarshal(hb, hr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
|
||||
@@ -240,7 +256,8 @@ func (c *Client) GetHeader(ctx context.Context, slot primitives.Slot, parentHash
|
||||
return nil, errors.Wrapf(err, "could not extract proto message from header")
|
||||
}
|
||||
return WrappedSignedBuilderBidCapella(p)
|
||||
case strings.ToLower(version.String(version.Bellatrix)):
|
||||
}
|
||||
if ver >= version.Bellatrix {
|
||||
hr := &ExecHeaderResponse{}
|
||||
if err := json.Unmarshal(hb, hr); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshaling the builder GetHeader response, using slot=%d, parentHash=%#x, pubkey=%#x", slot, parentHash, pubkey)
|
||||
@@ -250,9 +267,8 @@ func (c *Client) GetHeader(ctx context.Context, slot primitives.Slot, parentHash
|
||||
return nil, errors.Wrap(err, "could not extract proto message from header")
|
||||
}
|
||||
return WrappedSignedBuilderBid(p)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported header version %s", strings.ToLower(v.Version))
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported header version %s", strings.ToLower(v.Version))
|
||||
}
|
||||
|
||||
// RegisterValidator encodes the SignedValidatorRegistrationV1 message to json (including hex-encoding the byte
|
||||
|
||||
@@ -266,9 +266,9 @@ func TestClient_GetHeader(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
|
||||
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
|
||||
|
||||
kcgCommitments, err := bid.BlobKzgCommitments()
|
||||
require.NoError(t, err)
|
||||
dbid, ok := bid.(builderBidDeneb)
|
||||
require.Equal(t, true, ok)
|
||||
kcgCommitments := dbid.BlobKzgCommitments()
|
||||
require.Equal(t, len(kcgCommitments) > 0, true)
|
||||
for i := range kcgCommitments {
|
||||
require.Equal(t, len(kcgCommitments[i]) == 48, true)
|
||||
@@ -292,6 +292,50 @@ func TestClient_GetHeader(t *testing.T) {
|
||||
_, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
|
||||
require.ErrorContains(t, "could not extract proto message from header: too many blob commitments: 7", err)
|
||||
})
|
||||
t.Run("electra", func(t *testing.T) {
|
||||
hc := &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, expectedPath, r.URL.Path)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewBufferString(testExampleHeaderResponseElectra)),
|
||||
Request: r.Clone(ctx),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
c := &Client{
|
||||
hc: hc,
|
||||
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
|
||||
}
|
||||
h, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
|
||||
require.NoError(t, err)
|
||||
expectedWithdrawalsRoot := ezDecode(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2")
|
||||
bid, err := h.Message()
|
||||
require.NoError(t, err)
|
||||
bidHeader, err := bid.Header()
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot, err := bidHeader.WithdrawalsRoot()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, bytes.Equal(expectedWithdrawalsRoot, withdrawalsRoot))
|
||||
|
||||
bidStr := "652312848583266388373324160190187140051835877600158453279131187530910662656"
|
||||
value, err := stringToUint256(bidStr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, value.Int.Cmp(primitives.WeiToBigInt(bid.Value())))
|
||||
require.Equal(t, bidStr, primitives.WeiToBigInt(bid.Value()).String())
|
||||
ebid, ok := bid.(builderBidElectra)
|
||||
require.Equal(t, true, ok)
|
||||
kcgCommitments := ebid.BlobKzgCommitments()
|
||||
require.Equal(t, len(kcgCommitments) > 0, true)
|
||||
for i := range kcgCommitments {
|
||||
require.Equal(t, len(kcgCommitments[i]) == 48, true)
|
||||
}
|
||||
requests := ebid.ExecutionRequests()
|
||||
require.Equal(t, 1, len(requests.Deposits))
|
||||
require.Equal(t, 1, len(requests.Withdrawals))
|
||||
require.Equal(t, 1, len(requests.Consolidations))
|
||||
|
||||
})
|
||||
t.Run("unsupported version", func(t *testing.T) {
|
||||
hc := &http.Client{
|
||||
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
|
||||
|
||||
@@ -5,13 +5,15 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
||||
types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/math"
|
||||
@@ -414,54 +416,10 @@ func FromProtoDeneb(payload *v1.ExecutionPayloadDeneb) (ExecutionPayloadDeneb, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
var errInvalidTypeConversion = errors.New("unable to translate between api and foreign type")
|
||||
|
||||
// ExecutionPayloadResponseFromData converts an ExecutionData interface value to a payload response.
|
||||
// This involves serializing the execution payload value so that the abstract payload envelope can be used.
|
||||
func ExecutionPayloadResponseFromData(ed interfaces.ExecutionData, bundle *v1.BlobsBundle) (*ExecutionPayloadResponse, error) {
|
||||
pb := ed.Proto()
|
||||
var data interface{}
|
||||
var err error
|
||||
var ver string
|
||||
switch pbStruct := pb.(type) {
|
||||
case *v1.ExecutionPayload:
|
||||
ver = version.String(version.Bellatrix)
|
||||
data, err = FromProto(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Bellatrix ExecutionPayload to an API response")
|
||||
}
|
||||
case *v1.ExecutionPayloadCapella:
|
||||
ver = version.String(version.Capella)
|
||||
data, err = FromProtoCapella(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Capella ExecutionPayload to an API response")
|
||||
}
|
||||
case *v1.ExecutionPayloadDeneb:
|
||||
ver = version.String(version.Deneb)
|
||||
payloadStruct, err := FromProtoDeneb(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Deneb ExecutionPayload to an API response")
|
||||
}
|
||||
data = &ExecutionPayloadDenebAndBlobsBundle{
|
||||
ExecutionPayload: &payloadStruct,
|
||||
BlobsBundle: FromBundleProto(bundle),
|
||||
}
|
||||
default:
|
||||
return nil, errInvalidTypeConversion
|
||||
}
|
||||
encoded, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal execution payload version=%s", ver)
|
||||
}
|
||||
return &ExecutionPayloadResponse{
|
||||
Version: ver,
|
||||
Data: encoded,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExecHeaderResponseCapella is the response of builder API /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey} for Capella.
|
||||
type ExecHeaderResponseCapella struct {
|
||||
Data struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *BuilderBidCapella `json:"message"`
|
||||
} `json:"data"`
|
||||
@@ -605,17 +563,25 @@ type BlobBundler interface {
|
||||
BundleProto() (*v1.BlobsBundle, error)
|
||||
}
|
||||
|
||||
// ParsedExecutionRequests can retrieve the underlying execution requests for the given execution payload response.
|
||||
type ParsedExecutionRequests interface {
|
||||
ExecutionRequestsProto() (*v1.ExecutionRequests, error)
|
||||
}
|
||||
|
||||
func (r *ExecutionPayloadResponse) ParsePayload() (ParsedPayload, error) {
|
||||
var toProto ParsedPayload
|
||||
switch r.Version {
|
||||
case version.String(version.Bellatrix):
|
||||
toProto = &ExecutionPayload{}
|
||||
case version.String(version.Capella):
|
||||
toProto = &ExecutionPayloadCapella{}
|
||||
case version.String(version.Deneb):
|
||||
v, err := version.FromString(strings.ToLower(r.Version))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("unsupported version %s", strings.ToLower(r.Version)))
|
||||
}
|
||||
if v >= version.Deneb {
|
||||
toProto = &ExecutionPayloadDenebAndBlobsBundle{}
|
||||
default:
|
||||
return nil, consensusblocks.ErrUnsupportedVersion
|
||||
} else if v >= version.Capella {
|
||||
toProto = &ExecutionPayloadCapella{}
|
||||
} else if v >= version.Bellatrix {
|
||||
toProto = &ExecutionPayload{}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported version %s", strings.ToLower(r.Version))
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(r.Data, toProto); err != nil {
|
||||
@@ -990,7 +956,8 @@ func (ch *BLSToExecutionChange) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// ExecHeaderResponseDeneb is the header response for builder API /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}.
|
||||
type ExecHeaderResponseDeneb struct {
|
||||
Data struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *BuilderBidDeneb `json:"message"`
|
||||
} `json:"data"`
|
||||
@@ -1307,6 +1274,208 @@ func (p *ExecutionPayloadDeneb) ToProto() (*v1.ExecutionPayloadDeneb, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExecHeaderResponseElectra is the header response for builder API /eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}.
|
||||
type ExecHeaderResponseElectra struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *BuilderBidElectra `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ToProto creates a SignedBuilderBidElectra Proto from ExecHeaderResponseElectra.
|
||||
func (ehr *ExecHeaderResponseElectra) ToProto() (*eth.SignedBuilderBidElectra, error) {
|
||||
bb, err := ehr.Data.Message.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ð.SignedBuilderBidElectra{
|
||||
Message: bb,
|
||||
Signature: bytesutil.SafeCopyBytes(ehr.Data.Signature),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ToProto creates a BuilderBidElectra Proto from BuilderBidElectra.
|
||||
func (bb *BuilderBidElectra) ToProto() (*eth.BuilderBidElectra, error) {
|
||||
header, err := bb.Header.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(bb.BlobKzgCommitments) > params.BeaconConfig().MaxBlobsPerBlockByVersion(version.Electra) {
|
||||
return nil, fmt.Errorf("blob commitment count %d exceeds the maximum %d", len(bb.BlobKzgCommitments), params.BeaconConfig().MaxBlobsPerBlockByVersion(version.Electra))
|
||||
}
|
||||
kzgCommitments := make([][]byte, len(bb.BlobKzgCommitments))
|
||||
for i, commit := range bb.BlobKzgCommitments {
|
||||
if len(commit) != fieldparams.BLSPubkeyLength {
|
||||
return nil, fmt.Errorf("commitment length %d is not %d", len(commit), fieldparams.BLSPubkeyLength)
|
||||
}
|
||||
kzgCommitments[i] = bytesutil.SafeCopyBytes(commit)
|
||||
}
|
||||
// post electra execution requests should not be nil, if no requests exist use an empty request
|
||||
if bb.ExecutionRequests == nil {
|
||||
return nil, errors.New("bid contains nil execution requests")
|
||||
}
|
||||
executionRequests, err := bb.ExecutionRequests.ToProto()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert ExecutionRequests")
|
||||
}
|
||||
return ð.BuilderBidElectra{
|
||||
Header: header,
|
||||
BlobKzgCommitments: kzgCommitments,
|
||||
ExecutionRequests: executionRequests,
|
||||
// Note that SSZBytes() reverses byte order for the little-endian representation.
|
||||
// Uint256.Bytes() is big-endian, SSZBytes takes this value and reverses it.
|
||||
Value: bytesutil.SafeCopyBytes(bb.Value.SSZBytes()),
|
||||
Pubkey: bytesutil.SafeCopyBytes(bb.Pubkey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExecutionRequestsV1 is a wrapper for different execution requests
|
||||
type ExecutionRequestsV1 struct {
|
||||
Deposits []*DepositRequestV1 `json:"deposits"`
|
||||
Withdrawals []*WithdrawalRequestV1 `json:"withdrawals"`
|
||||
Consolidations []*ConsolidationRequestV1 `json:"consolidations"`
|
||||
}
|
||||
|
||||
func (er *ExecutionRequestsV1) ToProto() (*v1.ExecutionRequests, error) {
|
||||
if uint64(len(er.Deposits)) > params.BeaconConfig().MaxDepositRequestsPerPayload {
|
||||
return nil, fmt.Errorf("deposit requests count %d exceeds the maximum %d", len(er.Deposits), params.BeaconConfig().MaxDepositRequestsPerPayload)
|
||||
}
|
||||
deposits := make([]*v1.DepositRequest, len(er.Deposits))
|
||||
for i, dep := range er.Deposits {
|
||||
d, err := dep.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deposits[i] = d
|
||||
}
|
||||
if uint64(len(er.Withdrawals)) > params.BeaconConfig().MaxWithdrawalRequestsPerPayload {
|
||||
return nil, fmt.Errorf("withdrawal requests count %d exceeds the maximum %d", len(er.Withdrawals), params.BeaconConfig().MaxWithdrawalRequestsPerPayload)
|
||||
}
|
||||
withdrawals := make([]*v1.WithdrawalRequest, len(er.Withdrawals))
|
||||
for i, wr := range er.Withdrawals {
|
||||
w, err := wr.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawals[i] = w
|
||||
}
|
||||
if uint64(len(er.Consolidations)) > params.BeaconConfig().MaxConsolidationsRequestsPerPayload {
|
||||
return nil, fmt.Errorf("consolidation requests count %d exceeds the maximum %d", len(er.Consolidations), params.BeaconConfig().MaxConsolidationsRequestsPerPayload)
|
||||
}
|
||||
consolidations := make([]*v1.ConsolidationRequest, len(er.Consolidations))
|
||||
for i, con := range er.Consolidations {
|
||||
c, err := con.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
consolidations[i] = c
|
||||
}
|
||||
return &v1.ExecutionRequests{
|
||||
Deposits: deposits,
|
||||
Withdrawals: withdrawals,
|
||||
Consolidations: consolidations,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuilderBidElectra is a field of ExecHeaderResponseElectra.
|
||||
type BuilderBidElectra struct {
|
||||
Header *ExecutionPayloadHeaderDeneb `json:"header"`
|
||||
BlobKzgCommitments []hexutil.Bytes `json:"blob_kzg_commitments"`
|
||||
ExecutionRequests *ExecutionRequestsV1 `json:"execution_requests"`
|
||||
Value Uint256 `json:"value"`
|
||||
Pubkey hexutil.Bytes `json:"pubkey"`
|
||||
}
|
||||
|
||||
// WithdrawalRequestV1 is a field of ExecutionRequestsV1.
|
||||
type WithdrawalRequestV1 struct {
|
||||
SourceAddress hexutil.Bytes `json:"source_address"`
|
||||
ValidatorPubkey hexutil.Bytes `json:"validator_pubkey"`
|
||||
Amount Uint256 `json:"amount"`
|
||||
}
|
||||
|
||||
func (wr *WithdrawalRequestV1) ToProto() (*v1.WithdrawalRequest, error) {
|
||||
srcAddress, err := bytesutil.DecodeHexWithLength(wr.SourceAddress.String(), common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "source_address")
|
||||
}
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(wr.ValidatorPubkey.String(), fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "validator_pubkey")
|
||||
}
|
||||
|
||||
return &v1.WithdrawalRequest{
|
||||
SourceAddress: srcAddress,
|
||||
ValidatorPubkey: pubkey,
|
||||
Amount: wr.Amount.Uint64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DepositRequestV1 is a field of ExecutionRequestsV1.
|
||||
type DepositRequestV1 struct {
|
||||
PubKey hexutil.Bytes `json:"pubkey"`
|
||||
// withdrawalCredentials: DATA, 32 Bytes
|
||||
WithdrawalCredentials hexutil.Bytes `json:"withdrawal_credentials"`
|
||||
// amount: QUANTITY, 64 Bits
|
||||
Amount Uint256 `json:"amount"`
|
||||
// signature: DATA, 96 Bytes
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
// index: QUANTITY, 64 Bits
|
||||
Index Uint256 `json:"index"`
|
||||
}
|
||||
|
||||
func (dr *DepositRequestV1) ToProto() (*v1.DepositRequest, error) {
|
||||
pubkey, err := bytesutil.DecodeHexWithLength(dr.PubKey.String(), fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "pubkey")
|
||||
}
|
||||
wc, err := bytesutil.DecodeHexWithLength(dr.WithdrawalCredentials.String(), fieldparams.RootLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "withdrawal_credentials")
|
||||
}
|
||||
sig, err := bytesutil.DecodeHexWithLength(dr.Signature.String(), fieldparams.BLSSignatureLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "signature")
|
||||
}
|
||||
return &v1.DepositRequest{
|
||||
Pubkey: pubkey,
|
||||
WithdrawalCredentials: wc,
|
||||
Amount: dr.Amount.Uint64(),
|
||||
Signature: sig,
|
||||
Index: dr.Index.Uint64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConsolidationRequestV1 is a field of ExecutionRequestsV1.
|
||||
type ConsolidationRequestV1 struct {
|
||||
// sourceAddress: DATA, 20 Bytes
|
||||
SourceAddress hexutil.Bytes `json:"source_address"`
|
||||
// sourcePubkey: DATA, 48 Bytes
|
||||
SourcePubkey hexutil.Bytes `json:"source_pubkey"`
|
||||
// targetPubkey: DATA, 48 Bytes
|
||||
TargetPubkey hexutil.Bytes `json:"target_pubkey"`
|
||||
}
|
||||
|
||||
func (cr *ConsolidationRequestV1) ToProto() (*v1.ConsolidationRequest, error) {
|
||||
srcAddress, err := bytesutil.DecodeHexWithLength(cr.SourceAddress.String(), common.AddressLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "source_address")
|
||||
}
|
||||
sourcePubkey, err := bytesutil.DecodeHexWithLength(cr.SourcePubkey.String(), fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "source_pubkey")
|
||||
}
|
||||
targetPubkey, err := bytesutil.DecodeHexWithLength(cr.TargetPubkey.String(), fieldparams.BLSPubkeyLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "target_pubkey")
|
||||
}
|
||||
return &v1.ConsolidationRequest{
|
||||
SourceAddress: srcAddress,
|
||||
SourcePubkey: sourcePubkey,
|
||||
TargetPubkey: targetPubkey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ErrorMessage is a JSON representation of the builder API's returned error message.
|
||||
type ErrorMessage struct {
|
||||
Code int `json:"code"`
|
||||
|
||||
@@ -154,6 +154,64 @@ var testExampleHeaderResponseDeneb = `{
|
||||
}
|
||||
}`
|
||||
|
||||
var testExampleHeaderResponseElectra = `{
|
||||
"version": "electra",
|
||||
"data": {
|
||||
"message": {
|
||||
"header": {
|
||||
"parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
|
||||
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"block_number": "1",
|
||||
"gas_limit": "1",
|
||||
"gas_used": "1",
|
||||
"timestamp": "1",
|
||||
"extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"base_fee_per_gas": "1",
|
||||
"blob_gas_used": "1",
|
||||
"excess_blob_gas": "1",
|
||||
"block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"withdrawals_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
||||
},
|
||||
"blob_kzg_commitments": [
|
||||
"0xa94170080872584e54a1cf092d845703b13907f2e6b3b1c0ad573b910530499e3bcd48c6378846b80d2bfa58c81cf3d5"
|
||||
],
|
||||
"execution_requests": {
|
||||
"deposits": [
|
||||
{
|
||||
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
|
||||
"withdrawal_credentials": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"amount": "1",
|
||||
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
|
||||
"index": "1"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"source_address": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
|
||||
"validator_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
|
||||
"amount": "1"
|
||||
}
|
||||
],
|
||||
"consolidations": [
|
||||
{
|
||||
"source_address": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
|
||||
"source_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
|
||||
"target_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": "652312848583266388373324160190187140051835877600158453279131187530910662656",
|
||||
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"
|
||||
},
|
||||
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
||||
}
|
||||
}`
|
||||
|
||||
var testExampleHeaderResponseDenebNoBundle = `{
|
||||
"version": "deneb",
|
||||
"data": {
|
||||
@@ -1924,9 +1982,9 @@ func TestEmptyResponseBody(t *testing.T) {
|
||||
emptyResponse := &ExecutionPayloadResponse{}
|
||||
require.NoError(t, json.Unmarshal(empty, emptyResponse))
|
||||
_, err := emptyResponse.ParsePayload()
|
||||
require.ErrorIs(t, err, consensusblocks.ErrUnsupportedVersion)
|
||||
require.ErrorContains(t, "unsupported version", err)
|
||||
})
|
||||
versions := []int{version.Bellatrix, version.Capella, version.Deneb}
|
||||
versions := []int{version.Bellatrix, version.Capella, version.Deneb, version.Electra}
|
||||
for i := range versions {
|
||||
vstr := version.String(versions[i])
|
||||
t.Run("populated version without payload"+vstr, func(t *testing.T) {
|
||||
|
||||
@@ -268,6 +268,9 @@ func (s *Service) notifyNewPayload(ctx context.Context, preStateVersion int,
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not get execution requests")
|
||||
}
|
||||
if requests == nil {
|
||||
return false, errors.New("nil execution requests")
|
||||
}
|
||||
}
|
||||
lastValidHash, err = s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload, versionedHashes, parentRoot, requests)
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ type MockBuilderService struct {
|
||||
Bid *ethpb.SignedBuilderBid
|
||||
BidCapella *ethpb.SignedBuilderBidCapella
|
||||
BidDeneb *ethpb.SignedBuilderBidDeneb
|
||||
BidElectra *ethpb.SignedBuilderBidElectra
|
||||
RegistrationCache *cache.RegistrationCache
|
||||
ErrGetHeader error
|
||||
ErrRegisterValidator error
|
||||
@@ -59,7 +60,7 @@ func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.
|
||||
return nil, nil, errors.Wrap(err, "could not wrap capella payload")
|
||||
}
|
||||
return w, nil, s.ErrSubmitBlindedBlock
|
||||
case version.Deneb:
|
||||
case version.Deneb, version.Electra:
|
||||
w, err := blocks.WrappedExecutionPayloadDeneb(s.PayloadDeneb)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not wrap deneb payload")
|
||||
@@ -72,6 +73,9 @@ func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.
|
||||
|
||||
// GetHeader for mocking.
|
||||
func (s *MockBuilderService) GetHeader(_ context.Context, slot primitives.Slot, _ [32]byte, _ [48]byte) (builder.SignedBid, error) {
|
||||
if slots.ToEpoch(slot) >= params.BeaconConfig().ElectraForkEpoch || s.BidElectra != nil {
|
||||
return builder.WrappedSignedBuilderBidElectra(s.BidElectra)
|
||||
}
|
||||
if slots.ToEpoch(slot) >= params.BeaconConfig().DenebForkEpoch || s.BidDeneb != nil {
|
||||
return builder.WrappedSignedBuilderBidDeneb(s.BidDeneb)
|
||||
}
|
||||
|
||||
@@ -72,19 +72,11 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
}
|
||||
|
||||
var builderKzgCommitments [][]byte
|
||||
builderPayload, err := bid.Header()
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to retrieve header from BuilderBid")
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
}
|
||||
//TODO: add builder execution requests here.
|
||||
if bid.Version() >= version.Deneb {
|
||||
builderKzgCommitments, err = bid.BlobKzgCommitments()
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to retrieve kzg commitments from BuilderBid")
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case blk.Version() >= version.Capella:
|
||||
@@ -136,7 +128,28 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
|
||||
|
||||
// If we can't get the builder value, just use local block.
|
||||
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
|
||||
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
|
||||
var builderKzgCommitments [][]byte
|
||||
if bid.Version() >= version.Deneb {
|
||||
bidDeneb, ok := bid.(builder.BidDeneb)
|
||||
if !ok {
|
||||
log.Warnf("bid type %T does not implement builder.BidDeneb", bid)
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
} else {
|
||||
builderKzgCommitments = bidDeneb.BlobKzgCommitments()
|
||||
}
|
||||
}
|
||||
|
||||
var executionRequests *enginev1.ExecutionRequests
|
||||
if bid.Version() >= version.Electra {
|
||||
bidElectra, ok := bid.(builder.BidElectra)
|
||||
if !ok {
|
||||
log.Warnf("bid type %T does not implement builder.BidElectra", bid)
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
} else {
|
||||
executionRequests = bidElectra.ExecutionRequests()
|
||||
}
|
||||
}
|
||||
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments, executionRequests); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
} else {
|
||||
@@ -160,7 +173,7 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
|
||||
)
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
default: // Bellatrix case.
|
||||
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
|
||||
if err := setBuilderExecution(blk, builderPayload, nil, nil); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
|
||||
} else {
|
||||
@@ -270,23 +283,22 @@ func (vs *Server) getPayloadHeaderFromBuilder(
|
||||
return nil, errors.Wrap(err, "could not validate builder signature")
|
||||
}
|
||||
|
||||
maxBlobsPerBlock := params.BeaconConfig().MaxBlobsPerBlock(slot)
|
||||
var kzgCommitments [][]byte
|
||||
if bid.Version() >= version.Deneb {
|
||||
kzgCommitments, err = bid.BlobKzgCommitments()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get blob kzg commitments")
|
||||
}
|
||||
if len(kzgCommitments) > maxBlobsPerBlock {
|
||||
return nil, fmt.Errorf("builder returned too many kzg commitments: %d", len(kzgCommitments))
|
||||
}
|
||||
for _, c := range kzgCommitments {
|
||||
if len(c) != fieldparams.BLSPubkeyLength {
|
||||
return nil, fmt.Errorf("builder returned invalid kzg commitment length: %d", len(c))
|
||||
}
|
||||
dBid, ok := bid.(builder.BidDeneb)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bid type %T does not implement builder.BidDeneb", dBid)
|
||||
}
|
||||
kzgCommitments = dBid.BlobKzgCommitments()
|
||||
}
|
||||
var executionRequests *enginev1.ExecutionRequests
|
||||
if bid.Version() >= version.Electra {
|
||||
eBid, ok := bid.(builder.BidElectra)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bid type %T does not implement builder.BidElectra", eBid)
|
||||
}
|
||||
executionRequests = eBid.ExecutionRequests()
|
||||
}
|
||||
|
||||
l := log.WithFields(logrus.Fields{
|
||||
"gweiValue": primitives.WeiToGwei(v),
|
||||
"builderPubKey": fmt.Sprintf("%#x", bid.Pubkey()),
|
||||
@@ -298,6 +310,11 @@ func (vs *Server) getPayloadHeaderFromBuilder(
|
||||
if len(kzgCommitments) > 0 {
|
||||
l = l.WithField("kzgCommitmentCount", len(kzgCommitments))
|
||||
}
|
||||
if executionRequests != nil {
|
||||
l = l.WithField("depositRequestCount", len(executionRequests.Deposits))
|
||||
l = l.WithField("withdrawalRequestCount", len(executionRequests.Withdrawals))
|
||||
l = l.WithField("consolidationRequestCount", len(executionRequests.Consolidations))
|
||||
}
|
||||
l.Info("Received header with bid")
|
||||
|
||||
span.SetAttributes(
|
||||
@@ -366,20 +383,18 @@ func setLocalExecution(blk interfaces.SignedBeaconBlock, local *blocks.GetPayloa
|
||||
return errors.Wrap(err, "could not set execution requests")
|
||||
}
|
||||
}
|
||||
|
||||
return setExecution(blk, local.ExecutionData, false, kzgCommitments)
|
||||
return setExecution(blk, local.ExecutionData, false, kzgCommitments, local.ExecutionRequests)
|
||||
}
|
||||
|
||||
// setBuilderExecution sets the execution context for a builder's beacon block.
|
||||
// It delegates to setExecution for the actual work.
|
||||
func setBuilderExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, builderKzgCommitments [][]byte) error {
|
||||
// TODO #14344: add execution requests for electra
|
||||
return setExecution(blk, execution, true, builderKzgCommitments)
|
||||
func setBuilderExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, builderKzgCommitments [][]byte, requests *enginev1.ExecutionRequests) error {
|
||||
return setExecution(blk, execution, true, builderKzgCommitments, requests)
|
||||
}
|
||||
|
||||
// setExecution sets the execution context for a beacon block. It also sets KZG commitments based on the block version.
|
||||
// The function is designed to be flexible and handle both local and builder executions.
|
||||
func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, isBlinded bool, kzgCommitments [][]byte) error {
|
||||
func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, isBlinded bool, kzgCommitments [][]byte, requests *enginev1.ExecutionRequests) error {
|
||||
if execution == nil {
|
||||
return errors.New("execution is nil")
|
||||
}
|
||||
@@ -399,14 +414,27 @@ func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.Executi
|
||||
}
|
||||
|
||||
// Set the KZG commitments for the block
|
||||
errMessage = "failed to set local kzg commitments"
|
||||
kzgErr := "failed to set local kzg commitments"
|
||||
if isBlinded {
|
||||
errMessage = "failed to set builder kzg commitments"
|
||||
kzgErr = "failed to set builder kzg commitments"
|
||||
}
|
||||
if err := blk.SetBlobKzgCommitments(kzgCommitments); err != nil {
|
||||
return errors.Wrap(err, errMessage)
|
||||
return errors.Wrap(err, kzgErr)
|
||||
}
|
||||
|
||||
// If the block version is below Electra, no further actions are needed
|
||||
if blk.Version() < version.Electra {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the execution requests
|
||||
requestsErr := "failed to set local execution requests"
|
||||
if isBlinded {
|
||||
requestsErr = "failed to set builder execution requests"
|
||||
}
|
||||
if err := blk.SetExecutionRequests(requests); err != nil {
|
||||
return errors.Wrap(err, requestsErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
|
||||
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
@@ -173,11 +172,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -250,11 +244,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -326,11 +315,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, math.MaxUint64)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -402,11 +386,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, 0)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -428,11 +407,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -460,11 +434,6 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -493,13 +462,8 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit)
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
if builderBid.Version() >= version.Deneb {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err = builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
@@ -648,8 +612,9 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
builderPayload, err := builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
|
||||
require.NoError(t, err)
|
||||
dbid, ok := builderBid.(builder.BidDeneb)
|
||||
require.Equal(t, true, ok)
|
||||
builderKzgCommitments := dbid.BlobKzgCommitments()
|
||||
require.DeepEqual(t, bid.BlobKzgCommitments, builderKzgCommitments)
|
||||
require.Equal(t, bid.Header.BlockNumber, builderPayload.BlockNumber()) // header should be the same from block
|
||||
|
||||
@@ -663,6 +628,134 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bid.BlobKzgCommitments, got)
|
||||
})
|
||||
t.Run("Can get builder payload, blobs, and execution requests Electra", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockElectra())
|
||||
require.NoError(t, err)
|
||||
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
|
||||
require.NoError(t, err)
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
|
||||
|
||||
requests := &v1.ExecutionRequests{
|
||||
Deposits: []*v1.DepositRequest{
|
||||
{
|
||||
Pubkey: bytesutil.PadTo([]byte{byte('a')}, fieldparams.BLSPubkeyLength),
|
||||
WithdrawalCredentials: bytesutil.PadTo([]byte{byte('b')}, fieldparams.RootLength),
|
||||
Amount: params.BeaconConfig().MinActivationBalance,
|
||||
Signature: bytesutil.PadTo([]byte{byte('c')}, fieldparams.BLSSignatureLength),
|
||||
Index: 0,
|
||||
},
|
||||
},
|
||||
Withdrawals: []*v1.WithdrawalRequest{
|
||||
{
|
||||
SourceAddress: bytesutil.PadTo([]byte{byte('d')}, common.AddressLength),
|
||||
ValidatorPubkey: bytesutil.PadTo([]byte{byte('e')}, fieldparams.BLSPubkeyLength),
|
||||
Amount: params.BeaconConfig().MinActivationBalance,
|
||||
},
|
||||
},
|
||||
Consolidations: []*v1.ConsolidationRequest{
|
||||
{
|
||||
SourceAddress: bytesutil.PadTo([]byte{byte('f')}, common.AddressLength),
|
||||
SourcePubkey: bytesutil.PadTo([]byte{byte('g')}, fieldparams.BLSPubkeyLength),
|
||||
TargetPubkey: bytesutil.PadTo([]byte{byte('h')}, fieldparams.BLSPubkeyLength),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
bid := ðpb.BuilderBidElectra{
|
||||
Header: &v1.ExecutionPayloadHeaderDeneb{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
Timestamp: uint64(ti.Unix()),
|
||||
BlockNumber: 2,
|
||||
WithdrawalsRoot: wr[:],
|
||||
BlobGasUsed: 123,
|
||||
ExcessBlobGas: 456,
|
||||
GasLimit: gasLimit,
|
||||
},
|
||||
Pubkey: sk.PublicKey().Marshal(),
|
||||
Value: bytesutil.PadTo(builderValue, 32),
|
||||
BlobKzgCommitments: [][]byte{bytesutil.PadTo([]byte{2}, fieldparams.BLSPubkeyLength), bytesutil.PadTo([]byte{5}, fieldparams.BLSPubkeyLength)},
|
||||
ExecutionRequests: requests,
|
||||
}
|
||||
|
||||
d := params.BeaconConfig().DomainApplicationBuilder
|
||||
domain, err := signing.ComputeDomain(d, nil, nil)
|
||||
require.NoError(t, err)
|
||||
sr, err := signing.ComputeSigningRoot(bid, domain)
|
||||
require.NoError(t, err)
|
||||
sBid := ðpb.SignedBuilderBidElectra{
|
||||
Message: bid,
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidElectra: sBid,
|
||||
HasConfigured: true,
|
||||
Cfg: &builderTest.Config{BeaconDB: beaconDB},
|
||||
}
|
||||
require.NoError(t, beaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
|
||||
[]*ethpb.ValidatorRegistrationV1{{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
GasLimit: gasLimit,
|
||||
Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockElectra())
|
||||
require.NoError(t, err)
|
||||
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
|
||||
vs.ForkFetcher = chain
|
||||
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
|
||||
vs.TimeFetcher = chain
|
||||
vs.HeadFetcher = chain
|
||||
|
||||
ed, err := blocks.NewWrappedExecutionData(&v1.ExecutionPayloadDeneb{BlockNumber: 4, Withdrawals: withdrawals})
|
||||
require.NoError(t, err)
|
||||
vs.ExecutionEngineCaller = &powtesting.EngineClient{
|
||||
PayloadIDBytes: id,
|
||||
GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
blk.SetSlot(0)
|
||||
require.NoError(t, err)
|
||||
builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, blk.Block().Slot(), blk.Block().ProposerIndex(), gasLimit)
|
||||
require.NoError(t, err)
|
||||
builderPayload, err := builderBid.Header()
|
||||
require.NoError(t, err)
|
||||
eBid, ok := builderBid.(builder.BidElectra)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, bid.BlobKzgCommitments, eBid.BlobKzgCommitments())
|
||||
require.DeepEqual(t, bid.ExecutionRequests, eBid.ExecutionRequests())
|
||||
require.Equal(t, bid.Header.BlockNumber, builderPayload.BlockNumber()) // header should be the same from block
|
||||
|
||||
res, err := vs.getLocalPayload(ctx, blk.Block(), denebTransitionState)
|
||||
require.NoError(t, err)
|
||||
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, bundle)
|
||||
|
||||
got, err := blk.Block().Body().BlobKzgCommitments()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bid.BlobKzgCommitments, got)
|
||||
|
||||
gRequests, err := blk.Block().Body().ExecutionRequests()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bid.ExecutionRequests, gRequests)
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_getPayloadHeader(t *testing.T) {
|
||||
|
||||
@@ -661,8 +661,12 @@ func TestServer_GetBeaconBlock_Electra(t *testing.T) {
|
||||
ed, err := blocks.NewWrappedExecutionData(payload)
|
||||
require.NoError(t, err)
|
||||
proposerServer.ExecutionEngineCaller = &mockExecution.EngineClient{
|
||||
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
|
||||
GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed},
|
||||
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
|
||||
GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed, ExecutionRequests: &enginev1.ExecutionRequests{
|
||||
Withdrawals: wr,
|
||||
Deposits: dr,
|
||||
Consolidations: cr,
|
||||
}},
|
||||
}
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
@@ -786,8 +790,12 @@ func TestServer_GetBeaconBlock_Fulu(t *testing.T) {
|
||||
ed, err := blocks.NewWrappedExecutionData(payload)
|
||||
require.NoError(t, err)
|
||||
proposerServer.ExecutionEngineCaller = &mockExecution.EngineClient{
|
||||
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
|
||||
GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed},
|
||||
PayloadIDBytes: &enginev1.PayloadIDBytes{1},
|
||||
GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed, ExecutionRequests: &enginev1.ExecutionRequests{
|
||||
Withdrawals: wr,
|
||||
Deposits: dr,
|
||||
Consolidations: cr,
|
||||
}},
|
||||
}
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
|
||||
3
changelog/james-prysm_builder-electra.md
Normal file
3
changelog/james-prysm_builder-electra.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Builder API endpoint to support Electra
|
||||
@@ -68,6 +68,7 @@ go_test(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -395,6 +395,14 @@ func (b *BeaconChainConfig) MaxBlobsPerBlock(slot primitives.Slot) int {
|
||||
return b.DeprecatedMaxBlobsPerBlock
|
||||
}
|
||||
|
||||
// MaxBlobsPerBlockByVersion returns the maximum number of blobs per block for the given fork version
|
||||
func (b *BeaconChainConfig) MaxBlobsPerBlockByVersion(v int) int {
|
||||
if v >= version.Electra {
|
||||
return b.DeprecatedMaxBlobsPerBlockElectra
|
||||
}
|
||||
return b.DeprecatedMaxBlobsPerBlock
|
||||
}
|
||||
|
||||
// DenebEnabled centralizes the check to determine if code paths
|
||||
// that are specific to deneb should be allowed to execute. This will make it easier to find call sites that do this
|
||||
// kind of check and remove them post-deneb.
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/genesis"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
@@ -122,3 +123,36 @@ func Test_TargetBlobCount(t *testing.T) {
|
||||
require.Equal(t, cfg.TargetBlobsPerBlock(primitives.Slot(cfg.ElectraForkEpoch)*cfg.SlotsPerEpoch), 6)
|
||||
cfg.ElectraForkEpoch = math.MaxUint64
|
||||
}
|
||||
|
||||
func TestMaxBlobsPerBlockByVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
v int
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "Version below Electra",
|
||||
v: version.Electra - 1,
|
||||
want: params.BeaconConfig().DeprecatedMaxBlobsPerBlock,
|
||||
},
|
||||
{
|
||||
name: "Version equal to Electra",
|
||||
v: version.Electra,
|
||||
want: params.BeaconConfig().DeprecatedMaxBlobsPerBlockElectra,
|
||||
},
|
||||
{
|
||||
name: "Version above Electra",
|
||||
v: version.Electra + 1,
|
||||
want: params.BeaconConfig().DeprecatedMaxBlobsPerBlockElectra,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := params.BeaconConfig().MaxBlobsPerBlockByVersion(tt.v)
|
||||
if got != tt.want {
|
||||
t.Errorf("MaxBlobsPerBlockByVersion(%d) = %d, want %d", tt.v, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ ssz_electra_objs = [
|
||||
"BeaconStateElectra",
|
||||
"BlindedBeaconBlockBodyElectra",
|
||||
"BlindedBeaconBlockElectra",
|
||||
"BuilderBidElectra",
|
||||
"Consolidation",
|
||||
"IndexedAttestationElectra",
|
||||
"LightClientHeaderElectra",
|
||||
@@ -174,7 +175,7 @@ ssz_electra_objs = [
|
||||
"SignedBeaconBlockElectra",
|
||||
"SignedBlindedBeaconBlockElectra",
|
||||
"SignedConsolidation",
|
||||
"SingleAttestation",
|
||||
"SingleAttestation"
|
||||
]
|
||||
|
||||
ssz_fulu_objs = [
|
||||
|
||||
1479
proto/prysm/v1alpha1/beacon_block.pb.go
generated
1479
proto/prysm/v1alpha1/beacon_block.pb.go
generated
File diff suppressed because it is too large
Load Diff
@@ -816,6 +816,19 @@ message BuilderBidDeneb {
|
||||
bytes pubkey = 4 [(ethereum.eth.ext.ssz_size) = "48"];
|
||||
}
|
||||
|
||||
message BuilderBidElectra {
|
||||
ethereum.engine.v1.ExecutionPayloadHeaderDeneb header = 1;
|
||||
repeated bytes blob_kzg_commitments = 2 [(ethereum.eth.ext.ssz_size) = "?,48", (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size"];
|
||||
ethereum.engine.v1.ExecutionRequests execution_requests = 3;
|
||||
bytes value = 4 [(ethereum.eth.ext.ssz_size) = "32"];
|
||||
bytes pubkey = 5 [(ethereum.eth.ext.ssz_size) = "48"];
|
||||
}
|
||||
|
||||
message SignedBuilderBidElectra {
|
||||
BuilderBidElectra message = 1 ;
|
||||
bytes signature = 2 [(ethereum.eth.ext.ssz_size) = "96"];
|
||||
}
|
||||
|
||||
message BlobSidecars {
|
||||
repeated BlobSidecar sidecars = 1 [(ethereum.eth.ext.ssz_max) = "max_blobs_per_block.size"];
|
||||
}
|
||||
|
||||
@@ -659,6 +659,234 @@ func (s *SingleAttestation) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the BuilderBidElectra object
|
||||
func (b *BuilderBidElectra) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(b)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the BuilderBidElectra object to a target array
|
||||
func (b *BuilderBidElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(92)
|
||||
|
||||
// Offset (0) 'Header'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
if b.Header == nil {
|
||||
b.Header = new(v1.ExecutionPayloadHeaderDeneb)
|
||||
}
|
||||
offset += b.Header.SizeSSZ()
|
||||
|
||||
// Offset (1) 'BlobKzgCommitments'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(b.BlobKzgCommitments) * 48
|
||||
|
||||
// Offset (2) 'ExecutionRequests'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
if b.ExecutionRequests == nil {
|
||||
b.ExecutionRequests = new(v1.ExecutionRequests)
|
||||
}
|
||||
offset += b.ExecutionRequests.SizeSSZ()
|
||||
|
||||
// Field (3) 'Value'
|
||||
if size := len(b.Value); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("--.Value", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, b.Value...)
|
||||
|
||||
// Field (4) 'Pubkey'
|
||||
if size := len(b.Pubkey); size != 48 {
|
||||
err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48)
|
||||
return
|
||||
}
|
||||
dst = append(dst, b.Pubkey...)
|
||||
|
||||
// Field (0) 'Header'
|
||||
if dst, err = b.Header.MarshalSSZTo(dst); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (1) 'BlobKzgCommitments'
|
||||
if size := len(b.BlobKzgCommitments); size > 4096 {
|
||||
err = ssz.ErrListTooBigFn("--.BlobKzgCommitments", size, 4096)
|
||||
return
|
||||
}
|
||||
for ii := 0; ii < len(b.BlobKzgCommitments); ii++ {
|
||||
if size := len(b.BlobKzgCommitments[ii]); size != 48 {
|
||||
err = ssz.ErrBytesLengthFn("--.BlobKzgCommitments[ii]", size, 48)
|
||||
return
|
||||
}
|
||||
dst = append(dst, b.BlobKzgCommitments[ii]...)
|
||||
}
|
||||
|
||||
// Field (2) 'ExecutionRequests'
|
||||
if dst, err = b.ExecutionRequests.MarshalSSZTo(dst); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the BuilderBidElectra object
|
||||
func (b *BuilderBidElectra) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 92 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o0, o1, o2 uint64
|
||||
|
||||
// Offset (0) 'Header'
|
||||
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o0 != 92 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Offset (1) 'BlobKzgCommitments'
|
||||
if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
// Offset (2) 'ExecutionRequests'
|
||||
if o2 = ssz.ReadOffset(buf[8:12]); o2 > size || o1 > o2 {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
// Field (3) 'Value'
|
||||
if cap(b.Value) == 0 {
|
||||
b.Value = make([]byte, 0, len(buf[12:44]))
|
||||
}
|
||||
b.Value = append(b.Value, buf[12:44]...)
|
||||
|
||||
// Field (4) 'Pubkey'
|
||||
if cap(b.Pubkey) == 0 {
|
||||
b.Pubkey = make([]byte, 0, len(buf[44:92]))
|
||||
}
|
||||
b.Pubkey = append(b.Pubkey, buf[44:92]...)
|
||||
|
||||
// Field (0) 'Header'
|
||||
{
|
||||
buf = tail[o0:o1]
|
||||
if b.Header == nil {
|
||||
b.Header = new(v1.ExecutionPayloadHeaderDeneb)
|
||||
}
|
||||
if err = b.Header.UnmarshalSSZ(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Field (1) 'BlobKzgCommitments'
|
||||
{
|
||||
buf = tail[o1:o2]
|
||||
num, err := ssz.DivideInt2(len(buf), 48, 4096)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.BlobKzgCommitments = make([][]byte, num)
|
||||
for ii := 0; ii < num; ii++ {
|
||||
if cap(b.BlobKzgCommitments[ii]) == 0 {
|
||||
b.BlobKzgCommitments[ii] = make([]byte, 0, len(buf[ii*48:(ii+1)*48]))
|
||||
}
|
||||
b.BlobKzgCommitments[ii] = append(b.BlobKzgCommitments[ii], buf[ii*48:(ii+1)*48]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Field (2) 'ExecutionRequests'
|
||||
{
|
||||
buf = tail[o2:]
|
||||
if b.ExecutionRequests == nil {
|
||||
b.ExecutionRequests = new(v1.ExecutionRequests)
|
||||
}
|
||||
if err = b.ExecutionRequests.UnmarshalSSZ(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the BuilderBidElectra object
|
||||
func (b *BuilderBidElectra) SizeSSZ() (size int) {
|
||||
size = 92
|
||||
|
||||
// Field (0) 'Header'
|
||||
if b.Header == nil {
|
||||
b.Header = new(v1.ExecutionPayloadHeaderDeneb)
|
||||
}
|
||||
size += b.Header.SizeSSZ()
|
||||
|
||||
// Field (1) 'BlobKzgCommitments'
|
||||
size += len(b.BlobKzgCommitments) * 48
|
||||
|
||||
// Field (2) 'ExecutionRequests'
|
||||
if b.ExecutionRequests == nil {
|
||||
b.ExecutionRequests = new(v1.ExecutionRequests)
|
||||
}
|
||||
size += b.ExecutionRequests.SizeSSZ()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the BuilderBidElectra object
|
||||
func (b *BuilderBidElectra) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(b)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the BuilderBidElectra object with a hasher
|
||||
func (b *BuilderBidElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Header'
|
||||
if err = b.Header.HashTreeRootWith(hh); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (1) 'BlobKzgCommitments'
|
||||
{
|
||||
if size := len(b.BlobKzgCommitments); size > 4096 {
|
||||
err = ssz.ErrListTooBigFn("--.BlobKzgCommitments", size, 4096)
|
||||
return
|
||||
}
|
||||
subIndx := hh.Index()
|
||||
for _, i := range b.BlobKzgCommitments {
|
||||
if len(i) != 48 {
|
||||
err = ssz.ErrBytesLength
|
||||
return
|
||||
}
|
||||
hh.PutBytes(i)
|
||||
}
|
||||
|
||||
numItems := uint64(len(b.BlobKzgCommitments))
|
||||
hh.MerkleizeWithMixin(subIndx, numItems, 4096)
|
||||
}
|
||||
|
||||
// Field (2) 'ExecutionRequests'
|
||||
if err = b.ExecutionRequests.HashTreeRootWith(hh); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (3) 'Value'
|
||||
if size := len(b.Value); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("--.Value", size, 32)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(b.Value)
|
||||
|
||||
// Field (4) 'Pubkey'
|
||||
if size := len(b.Pubkey); size != 48 {
|
||||
err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(b.Pubkey)
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the SignedBeaconBlockContentsElectra object
|
||||
func (s *SignedBeaconBlockContentsElectra) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(s)
|
||||
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"//network/authorization:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//beacon/engine:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
@@ -21,6 +20,7 @@ import (
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/pkg/errors"
|
||||
builderAPI "github.com/prysmaticlabs/prysm/v5/api/client/builder"
|
||||
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/network/authorization"
|
||||
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
|
||||
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -56,6 +57,8 @@ const (
|
||||
GetPayloadMethodV2 = "engine_getPayloadV2"
|
||||
// GetPayloadMethodV3 v3 request string for JSON-RPC.
|
||||
GetPayloadMethodV3 = "engine_getPayloadV3"
|
||||
// GetPayloadMethodV4 v4 request string for JSON-RPC.
|
||||
GetPayloadMethodV4 = "engine_getPayloadV4"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -86,29 +89,13 @@ type ExecPayloadResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data *v1.ExecutionPayload `json:"data"`
|
||||
}
|
||||
|
||||
type ExecHeaderResponseCapella struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *builderAPI.BuilderBidCapella `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ExecHeaderResponseDeneb struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *builderAPI.BuilderBidDeneb `json:"message"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
cfg *config
|
||||
address string
|
||||
execClient *gethRPC.Client
|
||||
currId *v1.PayloadIDBytes
|
||||
prevBeaconRoot []byte
|
||||
currVersion int
|
||||
currPayload interfaces.ExecutionData
|
||||
blobBundle *v1.BlobsBundle
|
||||
mux *http.ServeMux
|
||||
@@ -334,6 +321,11 @@ func (p *Builder) handleHeaderRequest(w http.ResponseWriter, req *http.Request)
|
||||
}
|
||||
ax := types.Slot(slot)
|
||||
currEpoch := types.Epoch(ax / params.BeaconConfig().SlotsPerEpoch)
|
||||
if currEpoch >= params.BeaconConfig().ElectraForkEpoch {
|
||||
p.handleHeaderRequestElectra(w)
|
||||
return
|
||||
}
|
||||
|
||||
if currEpoch >= params.BeaconConfig().DenebForkEpoch {
|
||||
p.handleHeaderRequestDeneb(w)
|
||||
return
|
||||
@@ -414,6 +406,7 @@ func (p *Builder) handleHeaderRequest(w http.ResponseWriter, req *http.Request)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
p.currVersion = version.Bellatrix
|
||||
p.currPayload = wObj
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -474,7 +467,7 @@ func (p *Builder) handleHeaderRequestCapella(w http.ResponseWriter) {
|
||||
return
|
||||
}
|
||||
sig := secKey.Sign(rt[:])
|
||||
hdrResp := &ExecHeaderResponseCapella{
|
||||
hdrResp := &builderAPI.ExecHeaderResponseCapella{
|
||||
Version: "capella",
|
||||
Data: struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
@@ -491,6 +484,7 @@ func (p *Builder) handleHeaderRequestCapella(w http.ResponseWriter) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
p.currVersion = version.Capella
|
||||
p.currPayload = wObj
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -559,7 +553,7 @@ func (p *Builder) handleHeaderRequestDeneb(w http.ResponseWriter) {
|
||||
return
|
||||
}
|
||||
sig := secKey.Sign(rt[:])
|
||||
hdrResp := &ExecHeaderResponseDeneb{
|
||||
hdrResp := &builderAPI.ExecHeaderResponseDeneb{
|
||||
Version: "deneb",
|
||||
Data: struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
@@ -576,12 +570,148 @@ func (p *Builder) handleHeaderRequestDeneb(w http.ResponseWriter) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
p.currVersion = version.Deneb
|
||||
p.currPayload = wObj
|
||||
p.blobBundle = b.BlobsBundle
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (p *Builder) handleHeaderRequestElectra(w http.ResponseWriter) {
|
||||
b, err := p.retrievePendingBlockElectra()
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not retrieve pending block")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
secKey, err := bls.RandKey()
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not retrieve secret key")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
v := big.NewInt(0).SetBytes(bytesutil.ReverseByteOrder(b.Value))
|
||||
// we set the payload value as twice its actual one so that it always chooses builder payloads vs local payloads
|
||||
v = v.Mul(v, big.NewInt(2))
|
||||
wObj, err := blocks.WrappedExecutionPayloadDeneb(b.Payload)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not wrap execution payload")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
hdr, err := blocks.PayloadToHeaderElectra(wObj)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not make payload into header")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
val := builderAPI.Uint256{Int: v}
|
||||
var commitments []hexutil.Bytes
|
||||
for _, c := range b.BlobsBundle.KzgCommitments {
|
||||
copiedC := c
|
||||
commitments = append(commitments, copiedC)
|
||||
}
|
||||
wrappedHdr := &builderAPI.ExecutionPayloadHeaderDeneb{ExecutionPayloadHeaderDeneb: hdr}
|
||||
requests, err := b.GetDecodedExecutionRequests()
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not get decoded execution requests")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rv1 := &builderAPI.ExecutionRequestsV1{
|
||||
Deposits: make([]*builderAPI.DepositRequestV1, len(requests.Deposits)),
|
||||
Withdrawals: make([]*builderAPI.WithdrawalRequestV1, len(requests.Withdrawals)),
|
||||
Consolidations: make([]*builderAPI.ConsolidationRequestV1, len(requests.Consolidations)),
|
||||
}
|
||||
|
||||
for i, d := range requests.Deposits {
|
||||
amount := new(big.Int).SetUint64(d.Amount)
|
||||
index := new(big.Int).SetUint64(d.Index)
|
||||
dr := &builderAPI.DepositRequestV1{
|
||||
PubKey: d.Pubkey,
|
||||
WithdrawalCredentials: d.WithdrawalCredentials,
|
||||
Amount: builderAPI.Uint256{Int: amount},
|
||||
Signature: d.Signature,
|
||||
Index: builderAPI.Uint256{Int: index},
|
||||
}
|
||||
rv1.Deposits[i] = dr
|
||||
}
|
||||
|
||||
for i, w := range requests.Withdrawals {
|
||||
bi := new(big.Int).SetUint64(w.Amount)
|
||||
wr := &builderAPI.WithdrawalRequestV1{
|
||||
SourceAddress: w.SourceAddress,
|
||||
ValidatorPubkey: w.ValidatorPubkey,
|
||||
Amount: builderAPI.Uint256{Int: bi},
|
||||
}
|
||||
rv1.Withdrawals[i] = wr
|
||||
}
|
||||
|
||||
for i, c := range requests.Consolidations {
|
||||
cr := &builderAPI.ConsolidationRequestV1{
|
||||
SourceAddress: c.SourceAddress,
|
||||
SourcePubkey: c.SourcePubkey,
|
||||
TargetPubkey: c.TargetPubkey,
|
||||
}
|
||||
rv1.Consolidations[i] = cr
|
||||
}
|
||||
|
||||
bid := &builderAPI.BuilderBidElectra{
|
||||
Header: wrappedHdr,
|
||||
BlobKzgCommitments: commitments,
|
||||
Value: val,
|
||||
Pubkey: secKey.PublicKey().Marshal(),
|
||||
ExecutionRequests: rv1,
|
||||
}
|
||||
|
||||
sszBid := ð.BuilderBidElectra{
|
||||
Header: hdr,
|
||||
BlobKzgCommitments: b.BlobsBundle.KzgCommitments,
|
||||
Value: val.SSZBytes(),
|
||||
Pubkey: secKey.PublicKey().Marshal(),
|
||||
ExecutionRequests: requests,
|
||||
}
|
||||
d, err := signing.ComputeDomain(params.BeaconConfig().DomainApplicationBuilder,
|
||||
nil, /* fork version */
|
||||
nil /* genesis val root */)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not compute the domain")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rt, err := signing.ComputeSigningRoot(sszBid, d)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not compute the signing root")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sig := secKey.Sign(rt[:])
|
||||
hdrResp := &builderAPI.ExecHeaderResponseElectra{
|
||||
Version: "electra",
|
||||
Data: struct {
|
||||
Signature hexutil.Bytes `json:"signature"`
|
||||
Message *builderAPI.BuilderBidElectra `json:"message"`
|
||||
}{
|
||||
Signature: sig.Marshal(),
|
||||
Message: bid,
|
||||
},
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(hdrResp)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not encode response")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
p.currVersion = version.Electra
|
||||
p.currPayload = wObj
|
||||
p.blobBundle = b.BlobsBundle
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (p *Builder) handleBlindedBlock(w http.ResponseWriter, req *http.Request) {
|
||||
// TODO update for fork specific
|
||||
sb := &builderAPI.SignedBlindedBeaconBlockBellatrix{
|
||||
SignedBlindedBeaconBlockBellatrix: ð.SignedBlindedBeaconBlockBellatrix{},
|
||||
}
|
||||
@@ -596,7 +726,7 @@ func (p *Builder) handleBlindedBlock(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := builderAPI.ExecutionPayloadResponseFromData(p.currPayload, p.blobBundle)
|
||||
resp, err := ExecutionPayloadResponseFromData(p.currVersion, p.currPayload, p.blobBundle)
|
||||
if err != nil {
|
||||
p.cfg.logger.WithError(err).Error("Could not convert the payload")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -611,6 +741,48 @@ func (p *Builder) handleBlindedBlock(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
var errInvalidTypeConversion = errors.New("unable to translate between api and foreign type")
|
||||
|
||||
// ExecutionPayloadResponseFromData converts an ExecutionData interface value to a payload response.
|
||||
// This involves serializing the execution payload value so that the abstract payload envelope can be used.
|
||||
func ExecutionPayloadResponseFromData(v int, ed interfaces.ExecutionData, bundle *v1.BlobsBundle) (*builderAPI.ExecutionPayloadResponse, error) {
|
||||
pb := ed.Proto()
|
||||
var data interface{}
|
||||
var err error
|
||||
ver := version.String(v)
|
||||
switch pbStruct := pb.(type) {
|
||||
case *v1.ExecutionPayloadDeneb:
|
||||
payloadStruct, err := builderAPI.FromProtoDeneb(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Deneb ExecutionPayload to an API response")
|
||||
}
|
||||
data = &builderAPI.ExecutionPayloadDenebAndBlobsBundle{
|
||||
ExecutionPayload: &payloadStruct,
|
||||
BlobsBundle: builderAPI.FromBundleProto(bundle),
|
||||
}
|
||||
case *v1.ExecutionPayloadCapella:
|
||||
data, err = builderAPI.FromProtoCapella(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Capella ExecutionPayload to an API response")
|
||||
}
|
||||
case *v1.ExecutionPayload:
|
||||
data, err = builderAPI.FromProto(pbStruct)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert a Bellatrix ExecutionPayload to an API response")
|
||||
}
|
||||
default:
|
||||
return nil, errInvalidTypeConversion
|
||||
}
|
||||
encoded, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal execution payload version=%s", ver)
|
||||
}
|
||||
return &builderAPI.ExecutionPayloadResponse{
|
||||
Version: ver,
|
||||
Data: encoded,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Builder) retrievePendingBlock() (*v1.ExecutionPayload, error) {
|
||||
result := &engine.ExecutableData{}
|
||||
if p.currId == nil {
|
||||
@@ -620,7 +792,7 @@ func (p *Builder) retrievePendingBlock() (*v1.ExecutionPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadEnv, err := modifyExecutionPayload(*result, big.NewInt(0), nil)
|
||||
payloadEnv, err := modifyExecutionPayload(*result, big.NewInt(0), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -645,7 +817,7 @@ func (p *Builder) retrievePendingBlockCapella() (*v1.ExecutionPayloadCapellaWith
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadEnv, err := modifyExecutionPayload(*result.ExecutionPayload, result.BlockValue, nil)
|
||||
payloadEnv, err := modifyExecutionPayload(*result.ExecutionPayload, result.BlockValue, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -673,7 +845,7 @@ func (p *Builder) retrievePendingBlockDeneb() (*v1.ExecutionPayloadDenebWithValu
|
||||
if p.prevBeaconRoot == nil {
|
||||
p.cfg.logger.Errorf("previous root is nil")
|
||||
}
|
||||
payloadEnv, err := modifyExecutionPayload(*result.ExecutionPayload, result.BlockValue, p.prevBeaconRoot)
|
||||
payloadEnv, err := modifyExecutionPayload(*result.ExecutionPayload, result.BlockValue, p.prevBeaconRoot, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -690,6 +862,36 @@ func (p *Builder) retrievePendingBlockDeneb() (*v1.ExecutionPayloadDenebWithValu
|
||||
return denebPayload, nil
|
||||
}
|
||||
|
||||
func (p *Builder) retrievePendingBlockElectra() (*v1.ExecutionBundleElectra, error) {
|
||||
result := &engine.ExecutionPayloadEnvelope{}
|
||||
if p.currId == nil {
|
||||
return nil, errors.New("no payload id is cached")
|
||||
}
|
||||
err := p.execClient.CallContext(context.Background(), result, GetPayloadMethodV4, *p.currId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.prevBeaconRoot == nil {
|
||||
p.cfg.logger.Errorf("previous root is nil")
|
||||
}
|
||||
|
||||
payloadEnv, err := modifyExecutionPayload(*result.ExecutionPayload, result.BlockValue, p.prevBeaconRoot, result.Requests)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadEnv.BlobsBundle = result.BlobsBundle
|
||||
marshalledOutput, err := payloadEnv.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
electraPayload := &v1.ExecutionBundleElectra{}
|
||||
if err = json.Unmarshal(marshalledOutput, electraPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.currId = nil
|
||||
return electraPayload, nil
|
||||
}
|
||||
|
||||
func (p *Builder) sendHttpRequest(req *http.Request, requestBytes []byte) (*http.Response, error) {
|
||||
proxyReq, err := http.NewRequest(req.Method, p.cfg.destinationUrl.String(), req.Body)
|
||||
if err != nil {
|
||||
@@ -752,13 +954,12 @@ func unmarshalRPCObject(b []byte) (*jsonRPCObject, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func modifyExecutionPayload(execPayload engine.ExecutableData, fees *big.Int, prevBeaconRoot []byte) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
func modifyExecutionPayload(execPayload engine.ExecutableData, fees *big.Int, prevBeaconRoot []byte, requests [][]byte) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
modifiedBlock, err := executableDataToBlock(execPayload, prevBeaconRoot)
|
||||
if err != nil {
|
||||
return &engine.ExecutionPayloadEnvelope{}, err
|
||||
}
|
||||
// TODO: update to include requests for electra
|
||||
return engine.BlockToExecutableData(modifiedBlock, fees, nil /*blobs*/, nil /*requests*/), nil
|
||||
return engine.BlockToExecutableData(modifiedBlock, fees, nil /*blobs*/, requests /*requests*/), nil
|
||||
}
|
||||
|
||||
// This modifies the provided payload to imprint the builder's extra data
|
||||
@@ -775,6 +976,7 @@ func executableDataToBlock(params engine.ExecutableData, prevBeaconRoot []byte)
|
||||
h := gethTypes.DeriveSha(gethTypes.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
||||
withdrawalsRoot = &h
|
||||
}
|
||||
|
||||
header := &gethTypes.Header{
|
||||
ParentHash: params.ParentHash,
|
||||
UncleHash: gethTypes.EmptyUncleHash,
|
||||
@@ -799,7 +1001,7 @@ func executableDataToBlock(params engine.ExecutableData, prevBeaconRoot []byte)
|
||||
pRoot := common.Hash(prevBeaconRoot)
|
||||
header.ParentBeaconRoot = &pRoot
|
||||
}
|
||||
// TODO: update requests with requests for electra
|
||||
|
||||
body := gethTypes.Body{
|
||||
Transactions: txs,
|
||||
Uncles: nil,
|
||||
|
||||
Reference in New Issue
Block a user