diff --git a/proto/engine/v1/electra.go b/proto/engine/v1/electra.go index 386cce1bfe16..6c0005109a11 100644 --- a/proto/engine/v1/electra.go +++ b/proto/engine/v1/electra.go @@ -1,4 +1,134 @@ package enginev1 +import ( + "crypto/sha256" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v5/config/params" +) + type ExecutionPayloadElectra = ExecutionPayloadDeneb type ExecutionPayloadHeaderElectra = ExecutionPayloadHeaderDeneb + +// GetDecodedExecutionRequests decodes the byte array in ExecutionBundleElectra and returns a ExecutionRequests container object +// based on specifications from EIP-7685 +func (eb *ExecutionBundleElectra) GetDecodedExecutionRequests() (*ExecutionRequests, error) { + if len(eb.ExecutionRequests) == 0 { + return nil, errors.New("no execution requests found") + } + er := &ExecutionRequests{} + if err := processRequestBytes(eb.ExecutionRequests, er); err != nil { + return nil, err + } + + if err := verifyExecutionRequests(er); err != nil { + return nil, err + } + return er, nil +} + +func verifyExecutionRequests(er *ExecutionRequests) error { + if uint64(len(er.Deposits)) > params.BeaconConfig().MaxDepositRequestsPerPayload { + return fmt.Errorf("too many deposits requested: received %d, expected %d", len(er.Deposits), params.BeaconConfig().MaxDepositRequestsPerPayload) + } + if uint64(len(er.Withdrawals)) > params.BeaconConfig().MaxWithdrawalsPerPayload { + return fmt.Errorf("too many withdrawals requested: received %d, expected %d", len(er.Withdrawals), params.BeaconConfig().MaxWithdrawalRequestsPerPayload) + } + if uint64(len(er.Consolidations)) > params.BeaconConfig().MaxConsolidationsRequestsPerPayload { + return fmt.Errorf("too many consolidations requested: received %d, expected %d", len(er.Consolidations), params.BeaconConfig().MaxConsolidationsRequestsPerPayload) + } + return nil +} + +func processRequestBytes( + requestBytes []byte, + requests *ExecutionRequests, +) error { + if len(requestBytes) == 0 { + return nil // No more requests to process + } + + requestType := requestBytes[0] + remainingBytes := requestBytes[1:] + + switch requestType { + case 0: + dr := &DepositRequest{} + if len(remainingBytes) < dr.SizeSSZ() { + return fmt.Errorf("invalid deposit request size: returned %d, expected %d", len(remainingBytes), dr.SizeSSZ()) + } + drBytes := remainingBytes[:dr.SizeSSZ()] + remainingBytes = remainingBytes[dr.SizeSSZ():] + if err := dr.UnmarshalSSZ(drBytes); err != nil { + return errors.Wrap(err, "failed to unmarshal deposit request") + } + requests.Deposits = append(requests.Deposits, dr) + case 1: + wr := &WithdrawalRequest{} + if len(remainingBytes) < wr.SizeSSZ() { + return fmt.Errorf("invalid withdrawal request size: returned %d, expected %d", len(remainingBytes), wr.SizeSSZ()) + } + wrBytes := remainingBytes[:wr.SizeSSZ()] + remainingBytes = remainingBytes[wr.SizeSSZ():] + if err := wr.UnmarshalSSZ(wrBytes); err != nil { + return errors.Wrap(err, "failed to unmarshal withdrawal request") + } + requests.Withdrawals = append(requests.Withdrawals, wr) + case 2: + cr := &ConsolidationRequest{} + if len(remainingBytes) < cr.SizeSSZ() { + return fmt.Errorf("invalid consolidation request size: returned %d, expected %d", len(remainingBytes), cr.SizeSSZ()) + } + crBytes := remainingBytes[:cr.SizeSSZ()] + remainingBytes = remainingBytes[cr.SizeSSZ():] + if err := cr.UnmarshalSSZ(crBytes); err != nil { + return errors.Wrap(err, "failed to unmarshal consolidation request") + } + requests.Consolidations = append(requests.Consolidations, cr) + default: + return errors.New("invalid execution request type") + } + + // Recursive call with the remaining bytes + return processRequestBytes(remainingBytes, requests) +} + +// EncodeExecutionRequests is a helper function that takes a ExecutionRequests container and converts it into a hash used for `engine_NewPayloadV4` +func EncodeExecutionRequests(eb *ExecutionRequests) (common.Hash, error) { + var executionRequestBytes []byte + depositBytes, err := encodeExecutionRequestsByType(0, eb.Deposits) + if err != nil { + return common.Hash{}, errors.Wrap(err, "failed to encode deposit requests") + } + withdrawalBytes, err := encodeExecutionRequestsByType(1, eb.Withdrawals) + if err != nil { + return common.Hash{}, errors.Wrap(err, "failed to encode withdrawal requests") + } + consolidationBytes, err := encodeExecutionRequestsByType(2, eb.Consolidations) + if err != nil { + return common.Hash{}, errors.Wrap(err, "failed to encode consolidation requests") + } + executionRequestBytes = append(executionRequestBytes, depositBytes...) + executionRequestBytes = append(executionRequestBytes, withdrawalBytes...) + executionRequestBytes = append(executionRequestBytes, consolidationBytes...) + return sha256.Sum256(executionRequestBytes), nil +} + +type marshalSSZable interface { + MarshalSSZ() ([]byte, error) +} + +func encodeExecutionRequestsByType[T marshalSSZable](executionRequestType int, requests []T) ([]byte, error) { + var executionRequestBytes []byte + for _, er := range requests { + ssz, err := er.MarshalSSZ() + if err != nil { + return nil, err + } + executionRequestBytes = append(executionRequestBytes, byte(executionRequestType)) + executionRequestBytes = append(executionRequestBytes, ssz...) + } + return executionRequestBytes, nil +} diff --git a/proto/engine/v1/electra.pb.go b/proto/engine/v1/electra.pb.go index a23e9a678154..22bde8d8bfa9 100755 --- a/proto/engine/v1/electra.pb.go +++ b/proto/engine/v1/electra.pb.go @@ -290,6 +290,85 @@ func (x *ExecutionRequests) GetConsolidations() []*ConsolidationRequest { return nil } +type ExecutionBundleElectra struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload *ExecutionPayloadDeneb `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + BlobsBundle *BlobsBundle `protobuf:"bytes,3,opt,name=blobs_bundle,json=blobsBundle,proto3" json:"blobs_bundle,omitempty"` + ShouldOverrideBuilder bool `protobuf:"varint,4,opt,name=should_override_builder,json=shouldOverrideBuilder,proto3" json:"should_override_builder,omitempty"` + ExecutionRequests []byte `protobuf:"bytes,5,opt,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"` +} + +func (x *ExecutionBundleElectra) Reset() { + *x = ExecutionBundleElectra{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_engine_v1_electra_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecutionBundleElectra) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecutionBundleElectra) ProtoMessage() {} + +func (x *ExecutionBundleElectra) ProtoReflect() protoreflect.Message { + mi := &file_proto_engine_v1_electra_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecutionBundleElectra.ProtoReflect.Descriptor instead. +func (*ExecutionBundleElectra) Descriptor() ([]byte, []int) { + return file_proto_engine_v1_electra_proto_rawDescGZIP(), []int{4} +} + +func (x *ExecutionBundleElectra) GetPayload() *ExecutionPayloadDeneb { + if x != nil { + return x.Payload + } + return nil +} + +func (x *ExecutionBundleElectra) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *ExecutionBundleElectra) GetBlobsBundle() *BlobsBundle { + if x != nil { + return x.BlobsBundle + } + return nil +} + +func (x *ExecutionBundleElectra) GetShouldOverrideBuilder() bool { + if x != nil { + return x.ShouldOverrideBuilder + } + return false +} + +func (x *ExecutionBundleElectra) GetExecutionRequests() []byte { + if x != nil { + return x.ExecutionRequests + } + return nil +} + var File_proto_engine_v1_electra_proto protoreflect.FileDescriptor var file_proto_engine_v1_electra_proto_rawDesc = []byte{ @@ -298,64 +377,85 @@ var file_proto_engine_v1_electra_proto_rawDesc = []byte{ 0x12, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x8d, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0xc3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x16, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, - 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9f, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x73, 0x6f, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, - 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, - 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x87, 0x02, 0x0a, 0x11, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x48, - 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x42, 0x08, 0x92, 0xb5, 0x18, 0x04, 0x38, 0x31, 0x39, 0x32, 0x52, 0x08, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0b, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x12, 0x57, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, - 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x05, 0x92, 0xb5, 0x18, - 0x01, 0x31, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x42, 0x8e, 0x01, 0x0a, 0x16, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x45, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, - 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, - 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xaa, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, - 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x5c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, + 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, + 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, + 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x16, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x33, 0x32, 0x52, 0x15, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9f, + 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x75, + 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, + 0x34, 0x38, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x22, 0x87, 0x02, 0x0a, 0x11, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x48, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x08, 0x92, 0xb5, + 0x18, 0x04, 0x38, 0x31, 0x39, 0x32, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x12, 0x4f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x06, 0x92, 0xb5, + 0x18, 0x02, 0x31, 0x36, 0x52, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x73, 0x12, 0x57, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x73, + 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9e, 0x02, 0x0a, 0x16, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x6c, + 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, + 0x62, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x42, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, + 0x73, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x42, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x4f, 0x76, 0x65, + 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x12, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0x8e, 0x01, 0x0a, 0x16, + 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, + 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, + 0x76, 0x31, 0xaa, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x6e, + 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x5c, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -370,22 +470,27 @@ func file_proto_engine_v1_electra_proto_rawDescGZIP() []byte { return file_proto_engine_v1_electra_proto_rawDescData } -var file_proto_engine_v1_electra_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_proto_engine_v1_electra_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_engine_v1_electra_proto_goTypes = []interface{}{ - (*WithdrawalRequest)(nil), // 0: ethereum.engine.v1.WithdrawalRequest - (*DepositRequest)(nil), // 1: ethereum.engine.v1.DepositRequest - (*ConsolidationRequest)(nil), // 2: ethereum.engine.v1.ConsolidationRequest - (*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests + (*WithdrawalRequest)(nil), // 0: ethereum.engine.v1.WithdrawalRequest + (*DepositRequest)(nil), // 1: ethereum.engine.v1.DepositRequest + (*ConsolidationRequest)(nil), // 2: ethereum.engine.v1.ConsolidationRequest + (*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests + (*ExecutionBundleElectra)(nil), // 4: ethereum.engine.v1.ExecutionBundleElectra + (*ExecutionPayloadDeneb)(nil), // 5: ethereum.engine.v1.ExecutionPayloadDeneb + (*BlobsBundle)(nil), // 6: ethereum.engine.v1.BlobsBundle } var file_proto_engine_v1_electra_proto_depIdxs = []int32{ 1, // 0: ethereum.engine.v1.ExecutionRequests.deposits:type_name -> ethereum.engine.v1.DepositRequest 0, // 1: ethereum.engine.v1.ExecutionRequests.withdrawals:type_name -> ethereum.engine.v1.WithdrawalRequest 2, // 2: ethereum.engine.v1.ExecutionRequests.consolidations:type_name -> ethereum.engine.v1.ConsolidationRequest - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 5, // 3: ethereum.engine.v1.ExecutionBundleElectra.payload:type_name -> ethereum.engine.v1.ExecutionPayloadDeneb + 6, // 4: ethereum.engine.v1.ExecutionBundleElectra.blobs_bundle:type_name -> ethereum.engine.v1.BlobsBundle + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_proto_engine_v1_electra_proto_init() } @@ -393,6 +498,7 @@ func file_proto_engine_v1_electra_proto_init() { if File_proto_engine_v1_electra_proto != nil { return } + file_proto_engine_v1_execution_engine_proto_init() if !protoimpl.UnsafeEnabled { file_proto_engine_v1_electra_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WithdrawalRequest); i { @@ -442,6 +548,18 @@ func file_proto_engine_v1_electra_proto_init() { return nil } } + file_proto_engine_v1_electra_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecutionBundleElectra); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -449,7 +567,7 @@ func file_proto_engine_v1_electra_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_engine_v1_electra_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/engine/v1/electra.proto b/proto/engine/v1/electra.proto index 5b77f682d475..0a0dbbe55f33 100644 --- a/proto/engine/v1/electra.proto +++ b/proto/engine/v1/electra.proto @@ -16,6 +16,7 @@ syntax = "proto3"; package ethereum.engine.v1; import "proto/eth/ext/options.proto"; +import "proto/engine/v1/execution_engine.proto"; option csharp_namespace = "Ethereum.Engine.V1"; option go_package = "github.com/prysmaticlabs/prysm/v5/proto/engine/v1;enginev1"; @@ -60,7 +61,16 @@ message ConsolidationRequest { // ExecutionRequests is a container that contains all the requests from the execution layer to be included in a block message ExecutionRequests { - repeated DepositRequest deposits = 1 [(ethereum.eth.ext.ssz_max) = "max_deposit_requests_per_payload.size"]; + repeated DepositRequest deposits = 1 [(ethereum.eth.ext.ssz_max) = "max_deposit_requests_per_payload.size"]; repeated WithdrawalRequest withdrawals = 2 [(ethereum.eth.ext.ssz_max) = "max_withdrawal_requests_per_payload.size"]; - repeated ConsolidationRequest consolidations = 3 [(ethereum.eth.ext.ssz_max) = "max_consolidation_requests_per_payload.size"]; + repeated ConsolidationRequest consolidations = 3 [(ethereum.eth.ext.ssz_max) = "max_consolidation_requests_per_payload.size"]; } + +// ExecutionBundleElectra is a container that builds on Payload V4 and includes execution requests sidecar needed post Electra +message ExecutionBundleElectra { + ExecutionPayloadDeneb payload = 1; + bytes value = 2; + BlobsBundle blobs_bundle = 3; + bool should_override_builder = 4; + bytes execution_requests = 5; +} \ No newline at end of file diff --git a/proto/engine/v1/electra_test.go b/proto/engine/v1/electra_test.go new file mode 100644 index 000000000000..5ff3f6e90412 --- /dev/null +++ b/proto/engine/v1/electra_test.go @@ -0,0 +1,239 @@ +package enginev1_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/prysmaticlabs/prysm/v5/config/params" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +func TestGetDecodedExecutionRequests(t *testing.T) { + // Positive case + t.Run("Positive case: valid execution requests", func(t *testing.T) { + dr := &enginev1.DepositRequest{ + Pubkey: make([]byte, 48), + WithdrawalCredentials: make([]byte, 32), + Amount: 32000000000, + Signature: make([]byte, 96), + Index: 1, + } + drBytes, _ := dr.MarshalSSZ() + executionRequestBytes := append([]byte{0}, drBytes...) + eb := &enginev1.ExecutionBundleElectra{ + ExecutionRequests: executionRequestBytes, + } + er, err := eb.GetDecodedExecutionRequests() + require.NoError(t, err) + require.Equal(t, len(er.Deposits), 1) + }) + + // Negative case: empty ExecutionRequests + t.Run("Negative case: empty ExecutionRequests", func(t *testing.T) { + eb := &enginev1.ExecutionBundleElectra{ + ExecutionRequests: []byte{}, + } + _, err := eb.GetDecodedExecutionRequests() + require.ErrorContains(t, "no execution requests found", err) + }) + + // Negative case: invalid execution request type + t.Run("Negative case: invalid execution request type", func(t *testing.T) { + eb := &enginev1.ExecutionBundleElectra{ + ExecutionRequests: []byte{99}, + } + _, err := eb.GetDecodedExecutionRequests() + require.ErrorContains(t, "invalid execution request type", err) + }) + + // Negative case: verifyExecutionRequests returns error + t.Run("Negative case: verifyExecutionRequests error", func(t *testing.T) { + var executionRequestBytes []byte + for i := 0; i < int(params.BeaconConfig().MaxDepositRequestsPerPayload)+1; i++ { + dr := &enginev1.DepositRequest{ + Pubkey: make([]byte, 48), + WithdrawalCredentials: make([]byte, 32), + Amount: 32000000000, + Signature: make([]byte, 96), + Index: uint64(i), + } + drBytes, _ := dr.MarshalSSZ() + executionRequestBytes = append(executionRequestBytes, byte(0)) + executionRequestBytes = append(executionRequestBytes, drBytes...) + } + eb := &enginev1.ExecutionBundleElectra{ + ExecutionRequests: executionRequestBytes, + } + _, err := eb.GetDecodedExecutionRequests() + require.ErrorContains(t, "too many deposits requested", err) + }) +} + +func TestVerifyExecutionRequests(t *testing.T) { + // Positive case: counts within limits + t.Run("Positive case: counts within limits", func(t *testing.T) { + er := &enginev1.ExecutionRequests{ + Deposits: make([]*enginev1.DepositRequest, params.BeaconConfig().MaxDepositRequestsPerPayload), + Withdrawals: make([]*enginev1.WithdrawalRequest, params.BeaconConfig().MaxWithdrawalRequestsPerPayload), + Consolidations: make([]*enginev1.ConsolidationRequest, params.BeaconConfig().MaxConsolidationsRequestsPerPayload), + } + require.NoError(t, enginev1.VerifyExecutionRequests(er)) + }) + + // Negative case: too many deposits + t.Run("Negative case: too many deposits", func(t *testing.T) { + er := &enginev1.ExecutionRequests{ + Deposits: make([]*enginev1.DepositRequest, params.BeaconConfig().MaxDepositRequestsPerPayload+1), + } + err := enginev1.VerifyExecutionRequests(er) + require.ErrorContains(t, "too many deposits requested", err) + }) + + // Negative case: too many withdrawals + t.Run("Negative case: too many withdrawals", func(t *testing.T) { + er := &enginev1.ExecutionRequests{ + Withdrawals: make([]*enginev1.WithdrawalRequest, params.BeaconConfig().MaxWithdrawalRequestsPerPayload+1), + } + err := enginev1.VerifyExecutionRequests(er) + require.ErrorContains(t, "too many withdrawals requested", err) + }) + + // Negative case: too many consolidations + t.Run("Negative case: too many consolidations", func(t *testing.T) { + er := &enginev1.ExecutionRequests{ + Consolidations: make([]*enginev1.ConsolidationRequest, params.BeaconConfig().MaxConsolidationsRequestsPerPayload+1), + } + err := enginev1.VerifyExecutionRequests(er) + require.ErrorContains(t, "too many consolidations requested", err) + }) +} + +func TestProcessRequestBytes(t *testing.T) { + // Positive case: valid deposit request + t.Run("Positive case: valid mix of requests", func(t *testing.T) { + dr := &enginev1.DepositRequest{ + Pubkey: make([]byte, 48), + WithdrawalCredentials: make([]byte, 32), + Amount: 32000000000, + Signature: make([]byte, 96), + Index: 1, + } + drBytes, err := dr.MarshalSSZ() + require.NoError(t, err) + requestBytes := append([]byte{0}, drBytes...) + + wr := &enginev1.WithdrawalRequest{ + SourceAddress: make([]byte, 20), + ValidatorPubkey: make([]byte, 48), + Amount: 16000000000, + } + wrBytes, err := wr.MarshalSSZ() + require.NoError(t, err) + requestBytes = append(requestBytes, byte(1)) + requestBytes = append(requestBytes, wrBytes...) + + cr := enginev1.ConsolidationRequest{ + SourceAddress: make([]byte, 20), + SourcePubkey: make([]byte, 48), + TargetPubkey: make([]byte, 48), + } + crBytes, err := cr.MarshalSSZ() + require.NoError(t, err) + requestBytes = append(requestBytes, byte(2)) + requestBytes = append(requestBytes, crBytes...) + + requests := &enginev1.ExecutionRequests{} + err = enginev1.ProcessRequestBytes(requestBytes, requests) + require.NoError(t, err) + require.Equal(t, len(requests.Deposits), 1) + require.Equal(t, len(requests.Withdrawals), 1) + require.Equal(t, len(requests.Consolidations), 1) + }) + + // Negative case: invalid request type + t.Run("Negative case: invalid request type", func(t *testing.T) { + requestBytes := []byte{99} + requests := &enginev1.ExecutionRequests{} + err := enginev1.ProcessRequestBytes(requestBytes, requests) + require.ErrorContains(t, "invalid execution request type", err) + }) + + // Negative case: insufficient bytes for request + t.Run("Negative case: insufficient bytes for deposit request", func(t *testing.T) { + dr := &enginev1.DepositRequest{} + requestBytes := append([]byte{0}, make([]byte, dr.SizeSSZ()-1)...) + requests := &enginev1.ExecutionRequests{} + err := enginev1.ProcessRequestBytes(requestBytes, requests) + require.ErrorContains(t, "invalid deposit request size", err) + }) +} + +func TestEncodeExecutionRequests(t *testing.T) { + // Positive case: valid requests + t.Run("Positive case: valid requests", func(t *testing.T) { + eb := &enginev1.ExecutionRequests{ + Deposits: []*enginev1.DepositRequest{ + { + Pubkey: make([]byte, 48), + WithdrawalCredentials: make([]byte, 32), + Amount: 32000000000, + Signature: make([]byte, 96), + Index: 1, + }, + }, + Withdrawals: []*enginev1.WithdrawalRequest{ + { + SourceAddress: make([]byte, 20), + ValidatorPubkey: make([]byte, 48), + Amount: 16000000000, + }, + }, + Consolidations: []*enginev1.ConsolidationRequest{ + { + SourceAddress: make([]byte, 20), + SourcePubkey: make([]byte, 48), + TargetPubkey: make([]byte, 48), + }, + }, + } + hash, err := enginev1.EncodeExecutionRequests(eb) + require.NoError(t, err) + + require.NotEqual(t, hash.Cmp(common.Hash{}), 0) + }) + + // Negative case: MarshalSSZ returns error + t.Run("Negative case: deposit request MarshalSSZ error", func(t *testing.T) { + dr := &enginev1.DepositRequest{ + Pubkey: []byte{}, // Invalid length + } + eb := &enginev1.ExecutionRequests{ + Deposits: []*enginev1.DepositRequest{dr}, + } + _, err := enginev1.EncodeExecutionRequests(eb) + require.ErrorContains(t, "failed to encode deposit requests", err) + }) + + t.Run("Negative case: withdrawal request MarshalSSZ error", func(t *testing.T) { + wr := &enginev1.WithdrawalRequest{ + ValidatorPubkey: []byte{}, // Invalid length + } + eb := &enginev1.ExecutionRequests{ + Withdrawals: []*enginev1.WithdrawalRequest{wr}, + } + _, err := enginev1.EncodeExecutionRequests(eb) + require.ErrorContains(t, "failed to encode withdrawal requests", err) + }) + + t.Run("Negative case: consolidation request MarshalSSZ error", func(t *testing.T) { + cr := &enginev1.ConsolidationRequest{ + TargetPubkey: []byte{}, // Invalid length + } + eb := &enginev1.ExecutionRequests{ + Consolidations: []*enginev1.ConsolidationRequest{cr}, + } + _, err := enginev1.EncodeExecutionRequests(eb) + require.ErrorContains(t, "failed to encode consolidation requests", err) + }) +} diff --git a/proto/engine/v1/export_test.go b/proto/engine/v1/export_test.go index 17f4cde64607..0691eca1da19 100644 --- a/proto/engine/v1/export_test.go +++ b/proto/engine/v1/export_test.go @@ -1,3 +1,6 @@ package enginev1 type Copier[T any] copier[T] + +var VerifyExecutionRequests = verifyExecutionRequests +var ProcessRequestBytes = processRequestBytes