Add bundle v2 support for submit blind block (#15503)

* Add bundle v2 support for submit blind block

* add tests

---------

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
This commit is contained in:
Manu NALEPA
2025-07-17 09:51:28 +02:00
committed by GitHub
parent 9e014da0b9
commit 09485c2062
14 changed files with 945 additions and 336 deletions

View File

@@ -50,6 +50,7 @@ go_test(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",

View File

@@ -101,7 +101,7 @@ type BuilderClient interface {
NodeURL() string
GetHeader(ctx context.Context, slot primitives.Slot, parentHash [32]byte, pubkey [48]byte) (SignedBid, error)
RegisterValidator(ctx context.Context, svr []*ethpb.SignedValidatorRegistrationV1) error
SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error)
SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error)
Status(ctx context.Context) error
}
@@ -446,6 +446,9 @@ func sszValidatorRegisterRequest(svr []*ethpb.SignedValidatorRegistrationV1) ([]
var errResponseVersionMismatch = errors.New("builder API response uses a different version than requested in " + api.VersionHeader + " header")
func getVersionsBlockToPayload(blockVersion int) (int, error) {
if blockVersion >= version.Fulu {
return version.Fulu, nil
}
if blockVersion >= version.Deneb {
return version.Deneb, nil
}
@@ -460,7 +463,7 @@ func getVersionsBlockToPayload(blockVersion int) (int, error) {
// SubmitBlindedBlock calls the builder API endpoint that binds the validator to the builder and submits the block.
// The response is the full execution payload used to create the blinded block.
func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) {
body, postOpts, err := c.buildBlindedBlockRequest(sb)
if err != nil {
return nil, nil, err
@@ -558,7 +561,7 @@ func (c *Client) buildBlindedBlockRequest(sb interfaces.ReadOnlySignedBeaconBloc
func (c *Client) parseBlindedBlockResponse(
respBytes []byte,
forkVersion int,
) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
) (interfaces.ExecutionData, v1.BlobsBundler, error) {
if c.sszEnabled {
return c.parseBlindedBlockResponseSSZ(respBytes, forkVersion)
}
@@ -568,8 +571,18 @@ func (c *Client) parseBlindedBlockResponse(
func (c *Client) parseBlindedBlockResponseSSZ(
respBytes []byte,
forkVersion int,
) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
if forkVersion >= version.Deneb {
) (interfaces.ExecutionData, v1.BlobsBundler, error) {
if forkVersion >= version.Fulu {
payloadAndBlobs := &v1.ExecutionPayloadDenebAndBlobsBundleV2{}
if err := payloadAndBlobs.UnmarshalSSZ(respBytes); err != nil {
return nil, nil, errors.Wrap(err, "unable to unmarshal ExecutionPayloadDenebAndBlobsBundleV2 SSZ")
}
ed, err := blocks.NewWrappedExecutionData(payloadAndBlobs.Payload)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to wrap execution data for %s", version.String(forkVersion))
}
return ed, payloadAndBlobs.BlobsBundle, nil
} else if forkVersion >= version.Deneb {
payloadAndBlobs := &v1.ExecutionPayloadDenebAndBlobsBundle{}
if err := payloadAndBlobs.UnmarshalSSZ(respBytes); err != nil {
return nil, nil, errors.Wrap(err, "unable to unmarshal ExecutionPayloadDenebAndBlobsBundle SSZ")

View File

@@ -2,6 +2,7 @@ package builder
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
@@ -13,6 +14,7 @@ import (
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
v1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
@@ -1573,3 +1575,166 @@ func TestRequestLogger(t *testing.T) {
err = c.Status(ctx)
require.NoError(t, err)
}
func TestGetVersionsBlockToPayload(t *testing.T) {
tests := []struct {
name string
blockVersion int
expectedVersion int
expectedError bool
}{
{
name: "Fulu version",
blockVersion: 6, // version.Fulu
expectedVersion: 6,
expectedError: false,
},
{
name: "Deneb version",
blockVersion: 4, // version.Deneb
expectedVersion: 4,
expectedError: false,
},
{
name: "Capella version",
blockVersion: 3, // version.Capella
expectedVersion: 3,
expectedError: false,
},
{
name: "Bellatrix version",
blockVersion: 2, // version.Bellatrix
expectedVersion: 2,
expectedError: false,
},
{
name: "Unsupported version",
blockVersion: 0,
expectedError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
version, err := getVersionsBlockToPayload(tt.blockVersion)
if tt.expectedError {
assert.NotNil(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedVersion, version)
}
})
}
}
func TestParseBlindedBlockResponseSSZ_WithBlobsBundleV2(t *testing.T) {
c := &Client{sszEnabled: true}
// Create test payload
payload := &v1.ExecutionPayloadDeneb{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32),
ReceiptsRoot: make([]byte, 32),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
BlockNumber: 123456,
GasLimit: 30000000,
GasUsed: 21000,
Timestamp: 1234567890,
ExtraData: []byte("test-extra-data"),
BaseFeePerGas: make([]byte, 32),
BlockHash: make([]byte, 32),
Transactions: [][]byte{},
Withdrawals: []*v1.Withdrawal{},
BlobGasUsed: 1024,
ExcessBlobGas: 2048,
}
// Create test BlobsBundleV2
bundleV2 := &v1.BlobsBundleV2{
KzgCommitments: [][]byte{make([]byte, 48), make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48), make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072), make([]byte, 131072)},
}
// Test Fulu version (should use ExecutionPayloadDenebAndBlobsBundleV2)
t.Run("Fulu version with BlobsBundleV2", func(t *testing.T) {
payloadAndBlobsV2 := &v1.ExecutionPayloadDenebAndBlobsBundleV2{
Payload: payload,
BlobsBundle: bundleV2,
}
respBytes, err := payloadAndBlobsV2.MarshalSSZ()
require.NoError(t, err)
ed, bundle, err := c.parseBlindedBlockResponseSSZ(respBytes, 6) // version.Fulu
require.NoError(t, err)
require.NotNil(t, ed)
require.NotNil(t, bundle)
// Verify the bundle is BlobsBundleV2
bundleV2Result, ok := bundle.(*v1.BlobsBundleV2)
assert.Equal(t, true, ok, "Expected BlobsBundleV2 type")
require.Equal(t, len(bundleV2.KzgCommitments), len(bundleV2Result.KzgCommitments))
require.Equal(t, len(bundleV2.Proofs), len(bundleV2Result.Proofs))
require.Equal(t, len(bundleV2.Blobs), len(bundleV2Result.Blobs))
})
// Test Deneb version (should use regular BlobsBundle)
t.Run("Deneb version with regular BlobsBundle", func(t *testing.T) {
regularBundle := &v1.BlobsBundle{
KzgCommitments: bundleV2.KzgCommitments,
Proofs: bundleV2.Proofs,
Blobs: bundleV2.Blobs,
}
payloadAndBlobs := &v1.ExecutionPayloadDenebAndBlobsBundle{
Payload: payload,
BlobsBundle: regularBundle,
}
respBytes, err := payloadAndBlobs.MarshalSSZ()
require.NoError(t, err)
ed, bundle, err := c.parseBlindedBlockResponseSSZ(respBytes, 4) // version.Deneb
require.NoError(t, err)
require.NotNil(t, ed)
require.NotNil(t, bundle)
// Verify the bundle is regular BlobsBundle
regularBundleResult, ok := bundle.(*v1.BlobsBundle)
assert.Equal(t, true, ok, "Expected BlobsBundle type")
require.Equal(t, len(regularBundle.KzgCommitments), len(regularBundleResult.KzgCommitments))
})
// Test invalid SSZ data
t.Run("Invalid SSZ data", func(t *testing.T) {
invalidBytes := []byte("invalid-ssz-data")
ed, bundle, err := c.parseBlindedBlockResponseSSZ(invalidBytes, 6)
assert.NotNil(t, err)
assert.Equal(t, true, ed == nil)
assert.Equal(t, true, bundle == nil)
})
}
func TestSubmitBlindedBlock_BlobsBundlerInterface(t *testing.T) {
// Note: The full integration test is complex due to version detection logic
// The key functionality is tested in the parseBlindedBlockResponseSSZ tests above
// and in the mock service tests which verify the interface changes work correctly
t.Run("Interface signature verification", func(t *testing.T) {
// This test verifies that the SubmitBlindedBlock method signature
// has been updated to return BlobsBundler interface
client := &Client{}
// Verify the method exists with the correct signature
// by using reflection or by checking it compiles with the interface
var _ func(ctx context.Context, sb interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) = client.SubmitBlindedBlock
// This test passes if the signature is correct
assert.Equal(t, true, true)
})
}

View File

@@ -41,7 +41,7 @@ func (m MockClient) RegisterValidator(_ context.Context, svr []*ethpb.SignedVali
}
// SubmitBlindedBlock --
func (MockClient) SubmitBlindedBlock(_ context.Context, _ interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
func (MockClient) SubmitBlindedBlock(_ context.Context, _ interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) {
return nil, nil, nil
}

View File

@@ -24,7 +24,7 @@ var ErrNoBuilder = errors.New("builder endpoint not configured")
// BlockBuilder defines the interface for interacting with the block builder
type BlockBuilder interface {
SubmitBlindedBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error)
SubmitBlindedBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error)
GetHeader(ctx context.Context, slot primitives.Slot, parentHash [32]byte, pubKey [48]byte) (builder.SignedBid, error)
RegisterValidator(ctx context.Context, reg []*ethpb.SignedValidatorRegistrationV1) error
RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
@@ -87,7 +87,7 @@ func (s *Service) Stop() error {
}
// SubmitBlindedBlock submits a blinded block to the builder relay network.
func (s *Service) SubmitBlindedBlock(ctx context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
func (s *Service) SubmitBlindedBlock(ctx context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) {
ctx, span := trace.StartSpan(ctx, "builder.SubmitBlindedBlock")
defer span.End()
start := time.Now()

View File

@@ -29,6 +29,7 @@ type MockBuilderService struct {
PayloadCapella *v1.ExecutionPayloadCapella
PayloadDeneb *v1.ExecutionPayloadDeneb
BlobBundle *v1.BlobsBundle
BlobBundleV2 *v1.BlobsBundleV2
ErrSubmitBlindedBlock error
Bid *ethpb.SignedBuilderBid
BidCapella *ethpb.SignedBuilderBidCapella
@@ -46,7 +47,7 @@ func (s *MockBuilderService) Configured() bool {
}
// SubmitBlindedBlock for mocking.
func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, *v1.BlobsBundle, error) {
func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) {
switch b.Version() {
case version.Bellatrix:
w, err := blocks.WrappedExecutionPayload(s.Payload)
@@ -66,6 +67,16 @@ func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.
return nil, nil, errors.Wrap(err, "could not wrap deneb payload")
}
return w, s.BlobBundle, s.ErrSubmitBlindedBlock
case version.Fulu:
w, err := blocks.WrappedExecutionPayloadDeneb(s.PayloadDeneb)
if err != nil {
return nil, nil, errors.Wrap(err, "could not wrap deneb payload for fulu")
}
// For Fulu, return BlobsBundleV2 if available, otherwise regular BlobsBundle
if s.BlobBundleV2 != nil {
return w, s.BlobBundleV2, s.ErrSubmitBlindedBlock
}
return w, s.BlobBundle, s.ErrSubmitBlindedBlock
default:
return nil, nil, errors.New("unknown block version for mocking")
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/pkg/errors"
)
func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.BlobsBundle) ([]*ethpb.BlobSidecar, error) {
func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle enginev1.BlobsBundler) ([]*ethpb.BlobSidecar, error) {
if block.Version() < version.Deneb {
return nil, nil
}
@@ -33,26 +33,30 @@ func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.B
return nil, err
}
kzgCommitments := bundle.GetKzgCommitments()
blobs := bundle.GetBlobs()
proofs := bundle.GetProofs()
// Ensure there are equal counts of blobs/commitments/proofs.
if len(bundle.KzgCommitments) != len(bundle.Blobs) {
if len(kzgCommitments) != len(blobs) {
return nil, errors.New("mismatch commitments count")
}
if len(bundle.Proofs) != len(bundle.Blobs) {
if len(proofs) != len(blobs) {
return nil, errors.New("mismatch proofs count")
}
// Verify that commitments in the bundle match the block.
if len(bundle.KzgCommitments) != len(blockCommitments) {
if len(kzgCommitments) != len(blockCommitments) {
return nil, errors.New("commitment count doesn't match block")
}
for i, commitment := range blockCommitments {
if !bytes.Equal(bundle.KzgCommitments[i], commitment) {
if !bytes.Equal(kzgCommitments[i], commitment) {
return nil, errors.New("commitment value doesn't match block")
}
}
sidecars := make([]*ethpb.BlobSidecar, len(bundle.Blobs))
for i, b := range bundle.Blobs {
sidecars := make([]*ethpb.BlobSidecar, len(blobs))
for i, b := range blobs {
proof, err := consensusblocks.MerkleProofKZGCommitment(body, i)
if err != nil {
return nil, err
@@ -60,8 +64,8 @@ func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.B
sidecars[i] = &ethpb.BlobSidecar{
Index: uint64(i),
Blob: bytesutil.SafeCopyBytes(b),
KzgCommitment: bytesutil.SafeCopyBytes(bundle.KzgCommitments[i]),
KzgProof: bytesutil.SafeCopyBytes(bundle.Proofs[i]),
KzgCommitment: bytesutil.SafeCopyBytes(kzgCommitments[i]),
KzgProof: bytesutil.SafeCopyBytes(proofs[i]),
SignedBlockHeader: header,
CommitmentInclusionProof: proof,
}

View File

@@ -4,8 +4,10 @@ import (
"testing"
consensusblocks "github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
)
func TestUnblinder_UnblindBlobSidecars_InvalidBundle(t *testing.T) {
@@ -30,5 +32,122 @@ func TestUnblinder_UnblindBlobSidecars_InvalidBundle(t *testing.T) {
assert.NoError(t, err)
_, err = unblindBlobsSidecars(wBlock, nil)
assert.ErrorContains(t, "no valid bundle provided", err)
}
func TestUnblindBlobsSidecars_WithBlobsBundler(t *testing.T) {
// Test that the function accepts BlobsBundler interface
// This test focuses on the interface change rather than full integration
t.Run("Interface compatibility with BlobsBundle", func(t *testing.T) {
// Create a simple pre-Deneb block that will return nil (no processing needed)
wBlock, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{
Block: &ethpb.BeaconBlockCapella{
Body: &ethpb.BeaconBlockBodyCapella{},
},
Signature: nil,
})
require.NoError(t, err)
// Test with regular BlobsBundle
bundle := &enginev1.BlobsBundle{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
// This should work without error (returns nil for pre-Deneb)
sidecars, err := unblindBlobsSidecars(wBlock, bundle)
require.NoError(t, err)
assert.Equal(t, true, sidecars == nil)
})
t.Run("Interface compatibility with BlobsBundleV2", func(t *testing.T) {
// Create a simple pre-Deneb block that will return nil (no processing needed)
wBlock, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{
Block: &ethpb.BeaconBlockCapella{
Body: &ethpb.BeaconBlockBodyCapella{},
},
Signature: nil,
})
require.NoError(t, err)
// Test with BlobsBundleV2 - this is the key test for the interface change
bundleV2 := &enginev1.BlobsBundleV2{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
// This should work without error (returns nil for pre-Deneb)
sidecars, err := unblindBlobsSidecars(wBlock, bundleV2)
require.NoError(t, err)
assert.Equal(t, true, sidecars == nil)
})
t.Run("Function signature accepts BlobsBundler interface", func(t *testing.T) {
// This test verifies that the function signature has been updated to accept BlobsBundler
// We test this by verifying the code compiles with both types
// Create a simple pre-Deneb block for the interface test
wBlock, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{
Block: &ethpb.BeaconBlockCapella{
Body: &ethpb.BeaconBlockBodyCapella{},
},
Signature: nil,
})
require.NoError(t, err)
// Verify function accepts BlobsBundle through the interface
var regularBundle enginev1.BlobsBundler = &enginev1.BlobsBundle{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
_, err = unblindBlobsSidecars(wBlock, regularBundle)
require.NoError(t, err)
// Verify function accepts BlobsBundleV2 through the interface
var bundleV2 enginev1.BlobsBundler = &enginev1.BlobsBundleV2{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
_, err = unblindBlobsSidecars(wBlock, bundleV2)
require.NoError(t, err)
// If we get here, the interface change is working correctly
assert.Equal(t, true, true)
})
}
func TestUnblindBlobsSidecars_PreDenebBlock(t *testing.T) {
// Test with pre-Deneb block (should return nil sidecars)
wBlock, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockCapella{
Block: &ethpb.BeaconBlockCapella{
Body: &ethpb.BeaconBlockBodyCapella{},
},
Signature: nil,
})
require.NoError(t, err)
bundle := &enginev1.BlobsBundle{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
sidecars, err := unblindBlobsSidecars(wBlock, bundle)
require.NoError(t, err)
assert.Equal(t, true, sidecars == nil)
// Also test with BlobsBundleV2
bundleV2 := &enginev1.BlobsBundleV2{
KzgCommitments: [][]byte{make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072)},
}
sidecars, err = unblindBlobsSidecars(wBlock, bundleV2)
require.NoError(t, err)
assert.Equal(t, true, sidecars == nil)
}

View File

@@ -0,0 +1,2 @@
### Added
- Add support for parsing and handling `ExecutionPayloadAndBlobsBundleV2`.

View File

@@ -42,6 +42,7 @@ ssz_gen_marshal(
"ExecutionPayloadHeaderDeneb",
"ExecutionPayloadDeneb",
"ExecutionPayloadDenebAndBlobsBundle",
"ExecutionPayloadDenebAndBlobsBundleV2",
"BlindedBlobsBundle",
"BlobsBundle",
"BlobsBundleV2",

View File

@@ -1919,6 +1919,134 @@ func (e *ExecutionPayloadDenebAndBlobsBundle) HashTreeRootWith(hh *ssz.Hasher) (
return
}
// MarshalSSZ ssz marshals the ExecutionPayloadDenebAndBlobsBundleV2 object
func (e *ExecutionPayloadDenebAndBlobsBundleV2) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(e)
}
// MarshalSSZTo ssz marshals the ExecutionPayloadDenebAndBlobsBundleV2 object to a target array
func (e *ExecutionPayloadDenebAndBlobsBundleV2) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
offset := int(8)
// Offset (0) 'Payload'
dst = ssz.WriteOffset(dst, offset)
if e.Payload == nil {
e.Payload = new(ExecutionPayloadDeneb)
}
offset += e.Payload.SizeSSZ()
// Offset (1) 'BlobsBundle'
dst = ssz.WriteOffset(dst, offset)
if e.BlobsBundle == nil {
e.BlobsBundle = new(BlobsBundleV2)
}
offset += e.BlobsBundle.SizeSSZ()
// Field (0) 'Payload'
if dst, err = e.Payload.MarshalSSZTo(dst); err != nil {
return
}
// Field (1) 'BlobsBundle'
if dst, err = e.BlobsBundle.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the ExecutionPayloadDenebAndBlobsBundleV2 object
func (e *ExecutionPayloadDenebAndBlobsBundleV2) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 8 {
return ssz.ErrSize
}
tail := buf
var o0, o1 uint64
// Offset (0) 'Payload'
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
return ssz.ErrOffset
}
if o0 != 8 {
return ssz.ErrInvalidVariableOffset
}
// Offset (1) 'BlobsBundle'
if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
return ssz.ErrOffset
}
// Field (0) 'Payload'
{
buf = tail[o0:o1]
if e.Payload == nil {
e.Payload = new(ExecutionPayloadDeneb)
}
if err = e.Payload.UnmarshalSSZ(buf); err != nil {
return err
}
}
// Field (1) 'BlobsBundle'
{
buf = tail[o1:]
if e.BlobsBundle == nil {
e.BlobsBundle = new(BlobsBundleV2)
}
if err = e.BlobsBundle.UnmarshalSSZ(buf); err != nil {
return err
}
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the ExecutionPayloadDenebAndBlobsBundleV2 object
func (e *ExecutionPayloadDenebAndBlobsBundleV2) SizeSSZ() (size int) {
size = 8
// Field (0) 'Payload'
if e.Payload == nil {
e.Payload = new(ExecutionPayloadDeneb)
}
size += e.Payload.SizeSSZ()
// Field (1) 'BlobsBundle'
if e.BlobsBundle == nil {
e.BlobsBundle = new(BlobsBundleV2)
}
size += e.BlobsBundle.SizeSSZ()
return
}
// HashTreeRoot ssz hashes the ExecutionPayloadDenebAndBlobsBundleV2 object
func (e *ExecutionPayloadDenebAndBlobsBundleV2) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(e)
}
// HashTreeRootWith ssz hashes the ExecutionPayloadDenebAndBlobsBundleV2 object with a hasher
func (e *ExecutionPayloadDenebAndBlobsBundleV2) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'Payload'
if err = e.Payload.HashTreeRootWith(hh); err != nil {
return
}
// Field (1) 'BlobsBundle'
if err = e.BlobsBundle.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the ExecutionPayloadHeader object
func (e *ExecutionPayloadHeader) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(e)

File diff suppressed because it is too large Load Diff

View File

@@ -102,6 +102,11 @@ message ExecutionPayloadDenebAndBlobsBundle {
BlobsBundle blobs_bundle = 2;
}
message ExecutionPayloadDenebAndBlobsBundleV2 {
ExecutionPayloadDeneb payload = 1;
BlobsBundleV2 blobs_bundle = 2;
}
message ExecutionPayloadDenebWithValueAndBlobsBundle {
ExecutionPayloadDeneb payload = 1;
bytes value = 2;

View File

@@ -610,6 +610,85 @@ func TestJsonMarshalUnmarshal(t *testing.T) {
t.Run("execution bundle electra with deneb payload, blob data, and execution requests", func(t *testing.T) {
// TODO #14351: update this test when geth updates
})
t.Run("ExecutionPayloadDenebAndBlobsBundleV2 SSZ marshaling", func(t *testing.T) {
payload := &enginev1.ExecutionPayloadDeneb{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32),
ReceiptsRoot: make([]byte, 32),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
BlockNumber: 123,
GasLimit: 456,
GasUsed: 789,
Timestamp: 1000,
ExtraData: []byte("extra"),
BaseFeePerGas: bytesutil.PadTo(big.NewInt(1000000000).Bytes(), 32),
BlockHash: make([]byte, 32),
Transactions: [][]byte{},
Withdrawals: []*enginev1.Withdrawal{},
BlobGasUsed: 1024,
ExcessBlobGas: 2048,
}
bundleV2 := &enginev1.BlobsBundleV2{
KzgCommitments: [][]byte{make([]byte, 48), make([]byte, 48)},
Proofs: [][]byte{make([]byte, 48), make([]byte, 48)},
Blobs: [][]byte{make([]byte, 131072), make([]byte, 131072)},
}
bundle := &enginev1.ExecutionPayloadDenebAndBlobsBundleV2{
Payload: payload,
BlobsBundle: bundleV2,
}
sszBytes, err := bundle.MarshalSSZ()
require.NoError(t, err)
unmarshaled := &enginev1.ExecutionPayloadDenebAndBlobsBundleV2{}
err = unmarshaled.UnmarshalSSZ(sszBytes)
require.NoError(t, err)
require.DeepEqual(t, bundle.Payload.BlockNumber, unmarshaled.Payload.BlockNumber)
require.DeepEqual(t, bundle.Payload.GasLimit, unmarshaled.Payload.GasLimit)
require.DeepEqual(t, bundle.BlobsBundle.KzgCommitments, unmarshaled.BlobsBundle.KzgCommitments)
require.DeepEqual(t, bundle.BlobsBundle.Proofs, unmarshaled.BlobsBundle.Proofs)
})
t.Run("BlobsBundleV2 SSZ marshaling", func(t *testing.T) {
bundle := &enginev1.BlobsBundleV2{
KzgCommitments: [][]byte{
make([]byte, 48),
make([]byte, 48),
make([]byte, 48),
},
Proofs: [][]byte{
make([]byte, 48),
make([]byte, 48),
make([]byte, 48),
},
Blobs: [][]byte{
make([]byte, 131072),
make([]byte, 131072),
make([]byte, 131072),
},
}
sszBytes, err := bundle.MarshalSSZ()
require.NoError(t, err)
unmarshaled := &enginev1.BlobsBundleV2{}
err = unmarshaled.UnmarshalSSZ(sszBytes)
require.NoError(t, err)
require.Equal(t, len(bundle.KzgCommitments), len(unmarshaled.KzgCommitments))
require.Equal(t, len(bundle.Proofs), len(unmarshaled.Proofs))
require.Equal(t, len(bundle.Blobs), len(unmarshaled.Blobs))
require.DeepEqual(t, bundle.KzgCommitments, unmarshaled.KzgCommitments)
require.DeepEqual(t, bundle.Proofs, unmarshaled.Proofs)
require.DeepEqual(t, bundle.Blobs, unmarshaled.Blobs)
})
}
func TestPayloadIDBytes_MarshalUnmarshalJSON(t *testing.T) {