Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.25.7
require (
github.com/ethereum/go-ethereum v1.17.3
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a
github.com/ethpandaops/go-eth2-client v0.1.3
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1
github.com/ethpandaops/service-authenticatoor v0.0.1
github.com/glebarez/go-sqlite v1.22.0
github.com/goccy/go-yaml v1.19.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q
github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A=
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a h1:JEFHklBYWjZgxnTKWF7Iv7iSKgfHJVRDgA3Ar3ncUNI=
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a/go.mod h1:/DCIjcCCWKAUGu3UpmhjfchEq2MQCuWghu/crSEXzXc=
github.com/ethpandaops/go-eth2-client v0.1.3 h1:ZftRDaJfjT+fzgYAkOsFJmyWGnfkyKu7WYuXGLJtwQ8=
github.com/ethpandaops/go-eth2-client v0.1.3/go.mod h1:U3KdR8QSq8vqs9LWSGAF4ETHJpcB62E1DFf0gVMgWv0=
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1 h1:kQC/fTbBdZ8uZxFd779/EezbpWb6KMd+wPIlXjUMoDo=
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1/go.mod h1:U3KdR8QSq8vqs9LWSGAF4ETHJpcB62E1DFf0gVMgWv0=
github.com/ethpandaops/service-authenticatoor v0.0.1 h1:yZ0gKYf+kkj92piqqYprpF6HkhEIhCNmzDrsYth92KI=
github.com/ethpandaops/service-authenticatoor v0.0.1/go.mod h1:+C6sQMBxY2E3u6BITSZakQrGRovNnB1CamuQh/RIaXI=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
Expand Down
9 changes: 5 additions & 4 deletions pkg/builderapi/fulu/bid.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ package fulu
import (
"encoding/json"

eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
"github.com/ethpandaops/go-eth2-client/spec/deneb"
"github.com/ethpandaops/go-eth2-client/spec/electra"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
"github.com/ethpandaops/go-eth2-client/spec/version"
"github.com/holiman/uint256"
"github.com/pk910/dynamic-ssz/hasher"
"github.com/pk910/dynamic-ssz/sszutils"
Expand All @@ -16,7 +17,7 @@ import (
type BuilderBid struct {
Header *deneb.ExecutionPayloadHeader `json:"header"`
BlobKZGCommitments []deneb.KZGCommitment `json:"blob_kzg_commitments"`
ExecutionRequests *electra.ExecutionRequests `json:"execution_requests"`
ExecutionRequests *eth2all.ExecutionRequests `json:"execution_requests"`
Value *uint256.Int `json:"value"` // wei as uint256 in spec
Pubkey phase0.BLSPubKey `json:"pubkey"`
}
Expand Down Expand Up @@ -80,7 +81,7 @@ func (b *BuilderBid) HashTreeRootWith(hh sszutils.HashWalker) error {
return err
}
} else {
empty := &electra.ExecutionRequests{}
empty := &eth2all.ExecutionRequests{Version: version.DataVersionFulu}
if err := empty.HashTreeRootWith(hh); err != nil {
return err
}
Expand Down Expand Up @@ -116,7 +117,7 @@ type GetHeaderResponse struct {
type builderBidJSON struct {
Header *deneb.ExecutionPayloadHeader `json:"header"`
BlobKZGCommitments []deneb.KZGCommitment `json:"blob_kzg_commitments"`
ExecutionRequests *electra.ExecutionRequests `json:"execution_requests"`
ExecutionRequests *eth2all.ExecutionRequests `json:"execution_requests"`
Value string `json:"value"`
Pubkey phase0.BLSPubKey `json:"pubkey"`
}
Expand Down
13 changes: 9 additions & 4 deletions pkg/builderapi/fulu/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
package fulu

import (
"github.com/ethpandaops/go-eth2-client/spec/electra"
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
"github.com/ethpandaops/go-eth2-client/spec/version"
"github.com/holiman/uint256"

"github.com/ethpandaops/buildoor/pkg/payload_builder"
Expand Down Expand Up @@ -36,9 +37,13 @@ func BuildSignedBuilderBid(
return nil, err
}

execRequests := event.ExecutionRequests
if execRequests == nil {
execRequests = &electra.ExecutionRequests{}
// Fulu Builder API wire format uses the Electra layout; builder
// deposit/exit requests (Gloas+) do not exist in this spec version.
execRequests := &eth2all.ExecutionRequests{Version: version.DataVersionFulu}
if event.ExecutionRequests != nil {
execRequests.Deposits = event.ExecutionRequests.Deposits
execRequests.Withdrawals = event.ExecutionRequests.Withdrawals
execRequests.Consolidations = event.ExecutionRequests.Consolidations
}

value, overflow := uint256.FromBig(event.BlockValue)
Expand Down
3 changes: 1 addition & 2 deletions pkg/payload_bidder/bid.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
"github.com/ethpandaops/go-eth2-client/spec/bellatrix"
"github.com/ethpandaops/go-eth2-client/spec/deneb"
"github.com/ethpandaops/go-eth2-client/spec/electra"
"github.com/ethpandaops/go-eth2-client/spec/gloas"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
dynssz "github.com/pk910/dynamic-ssz"
Expand Down Expand Up @@ -38,7 +37,7 @@ func BuildSignedBid(
) (*eth2all.SignedExecutionPayloadBid, error) {
execRequests := p.ExecutionRequests
if execRequests == nil {
execRequests = &electra.ExecutionRequests{}
execRequests = &eth2all.ExecutionRequests{Version: p.ExecutionPayload.Version}
}

// dynssz so preset-dependent list limits resolve from the global spec.
Expand Down
7 changes: 1 addition & 6 deletions pkg/payload_bidder/reveal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"

eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
"github.com/ethpandaops/go-eth2-client/spec/electra"
"github.com/ethpandaops/go-eth2-client/spec/gloas"
"github.com/ethpandaops/go-eth2-client/spec/phase0"

Expand Down Expand Up @@ -33,11 +32,7 @@ func BuildSignedEnvelope(
) (signed *eth2all.SignedExecutionPayloadEnvelope, blobs, proofs [][]byte, err error) {
execRequests := p.ExecutionRequests
if execRequests == nil {
execRequests = &electra.ExecutionRequests{
Deposits: []*electra.DepositRequest{},
Withdrawals: []*electra.WithdrawalRequest{},
Consolidations: []*electra.ConsolidationRequest{},
}
execRequests = &eth2all.ExecutionRequests{Version: p.ExecutionPayload.Version}
}

envelope := &eth2all.ExecutionPayloadEnvelope{
Expand Down
109 changes: 99 additions & 10 deletions pkg/payload_builder/execution_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,36 @@ import (
"github.com/ethpandaops/go-eth-engine-client/spec/prague"
"github.com/ethpandaops/go-eth2-client/spec/bellatrix"
"github.com/ethpandaops/go-eth2-client/spec/electra"
"github.com/ethpandaops/go-eth2-client/spec/gloas"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
"github.com/ethpandaops/go-eth2-client/spec/version"

eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
)

const (
depositRequestType = 0x00
withdrawalRequestType = 0x01
consolidationRequestType = 0x02

depositRequestSize = 192 // 48 + 32 + 8 + 96 + 8
withdrawalRequestSize = 76 // 20 + 48 + 8
consolidationRequestSize = 116 // 20 + 48 + 48
depositRequestType = 0x00
withdrawalRequestType = 0x01
consolidationRequestType = 0x02
builderDepositRequestType = 0x03 // EIP-8282, Gloas+
builderExitRequestType = 0x04 // EIP-8282, Gloas+

depositRequestSize = 192 // 48 + 32 + 8 + 96 + 8
withdrawalRequestSize = 76 // 20 + 48 + 8
consolidationRequestSize = 116 // 20 + 48 + 48
builderDepositRequestSize = 184 // 48 + 32 + 8 + 96
builderExitRequestSize = 68 // 20 + 48
)

// ParseExecutionRequests decodes raw EIP-7685 execution request bytes from the
// Engine API into typed electra.ExecutionRequests.
// Engine API into a versioned eth2all.ExecutionRequests.
//
// Each element in raw is: [type_prefix_byte || request_1 || request_2 || ...]
// where all requests of the same type are concatenated after a single prefix byte.
func ParseExecutionRequests(raw []prague.ExecutionRequest) (*electra.ExecutionRequests, error) {
result := &electra.ExecutionRequests{
// Builder deposit (0x03) and builder exit (0x04) requests are only valid from Gloas onwards.
func ParseExecutionRequests(raw []prague.ExecutionRequest, dataVersion version.DataVersion) (*eth2all.ExecutionRequests, error) {
result := &eth2all.ExecutionRequests{
Version: dataVersion,
Deposits: make([]*electra.DepositRequest, 0),
Withdrawals: make([]*electra.WithdrawalRequest, 0),
Consolidations: make([]*electra.ConsolidationRequest, 0),
Expand Down Expand Up @@ -68,6 +78,26 @@ func ParseExecutionRequests(raw []prague.ExecutionRequest) (*electra.ExecutionRe
}
result.Consolidations = consolidations

case builderDepositRequestType:
if dataVersion < version.DataVersionGloas {
return nil, fmt.Errorf("execution request %d: builder deposit request not valid before Gloas", i)
}
builderDeposits, err := parseBuilderDepositRequests(data)
if err != nil {
return nil, fmt.Errorf("execution request %d: %w", i, err)
}
result.BuilderDeposits = builderDeposits

case builderExitRequestType:
if dataVersion < version.DataVersionGloas {
return nil, fmt.Errorf("execution request %d: builder exit request not valid before Gloas", i)
}
builderExits, err := parseBuilderExitRequests(data)
if err != nil {
return nil, fmt.Errorf("execution request %d: %w", i, err)
}
result.BuilderExits = builderExits

default:
return nil, fmt.Errorf("execution request %d: unknown type 0x%02x", i, reqType)
}
Expand Down Expand Up @@ -170,3 +200,62 @@ func parseConsolidationRequests(data []byte) ([]*electra.ConsolidationRequest, e

return consolidations, nil
}

func parseBuilderDepositRequests(data []byte) ([]*gloas.BuilderDepositRequest, error) {
if len(data)%builderDepositRequestSize != 0 {
return nil, fmt.Errorf("builder deposit requests: length %d not divisible by %d", len(data), builderDepositRequestSize)
}

count := len(data) / builderDepositRequestSize
deposits := make([]*gloas.BuilderDepositRequest, count)

for i := range count {
d := data[i*builderDepositRequestSize : (i+1)*builderDepositRequestSize]

var pubkey phase0.BLSPubKey
copy(pubkey[:], d[0:48])

withdrawalCreds := make([]byte, 32)
copy(withdrawalCreds, d[48:80])

amount := phase0.Gwei(binary.LittleEndian.Uint64(d[80:88]))

var sig phase0.BLSSignature
copy(sig[:], d[88:184])

deposits[i] = &gloas.BuilderDepositRequest{
Pubkey: pubkey,
WithdrawalCredentials: withdrawalCreds,
Amount: amount,
Signature: sig,
}
}

return deposits, nil
}

func parseBuilderExitRequests(data []byte) ([]*gloas.BuilderExitRequest, error) {
if len(data)%builderExitRequestSize != 0 {
return nil, fmt.Errorf("builder exit requests: length %d not divisible by %d", len(data), builderExitRequestSize)
}

count := len(data) / builderExitRequestSize
exits := make([]*gloas.BuilderExitRequest, count)

for i := range count {
d := data[i*builderExitRequestSize : (i+1)*builderExitRequestSize]

var addr bellatrix.ExecutionAddress
copy(addr[:], d[0:20])

var pubkey phase0.BLSPubKey
copy(pubkey[:], d[20:68])

exits[i] = &gloas.BuilderExitRequest{
SourceAddress: addr,
Pubkey: pubkey,
}
}

return exits, nil
}
Loading