Skip to content

Commit 9bf69b9

Browse files
authored
Merge pull request #103 from ethpandaops/bharath/eip-8282
EIP-8282: Support builder deposits and builder exits
2 parents 9d32bbb + 3ffbdb9 commit 9bf69b9

10 files changed

Lines changed: 213 additions & 45 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.25.7
55
require (
66
github.com/ethereum/go-ethereum v1.17.3
77
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a
8-
github.com/ethpandaops/go-eth2-client v0.1.3
8+
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1
99
github.com/ethpandaops/service-authenticatoor v0.0.1
1010
github.com/glebarez/go-sqlite v1.22.0
1111
github.com/goccy/go-yaml v1.19.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q
6868
github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A=
6969
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a h1:JEFHklBYWjZgxnTKWF7Iv7iSKgfHJVRDgA3Ar3ncUNI=
7070
github.com/ethpandaops/go-eth-engine-client v0.0.0-20260616025304-fcd77b340b2a/go.mod h1:/DCIjcCCWKAUGu3UpmhjfchEq2MQCuWghu/crSEXzXc=
71-
github.com/ethpandaops/go-eth2-client v0.1.3 h1:ZftRDaJfjT+fzgYAkOsFJmyWGnfkyKu7WYuXGLJtwQ8=
72-
github.com/ethpandaops/go-eth2-client v0.1.3/go.mod h1:U3KdR8QSq8vqs9LWSGAF4ETHJpcB62E1DFf0gVMgWv0=
71+
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1 h1:kQC/fTbBdZ8uZxFd779/EezbpWb6KMd+wPIlXjUMoDo=
72+
github.com/ethpandaops/go-eth2-client v0.1.4-0.20260618225213-e8d135e8cdc1/go.mod h1:U3KdR8QSq8vqs9LWSGAF4ETHJpcB62E1DFf0gVMgWv0=
7373
github.com/ethpandaops/service-authenticatoor v0.0.1 h1:yZ0gKYf+kkj92piqqYprpF6HkhEIhCNmzDrsYth92KI=
7474
github.com/ethpandaops/service-authenticatoor v0.0.1/go.mod h1:+C6sQMBxY2E3u6BITSZakQrGRovNnB1CamuQh/RIaXI=
7575
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=

pkg/builderapi/fulu/bid.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ package fulu
44
import (
55
"encoding/json"
66

7+
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
78
"github.com/ethpandaops/go-eth2-client/spec/deneb"
8-
"github.com/ethpandaops/go-eth2-client/spec/electra"
99
"github.com/ethpandaops/go-eth2-client/spec/phase0"
10+
"github.com/ethpandaops/go-eth2-client/spec/version"
1011
"github.com/holiman/uint256"
1112
"github.com/pk910/dynamic-ssz/hasher"
1213
"github.com/pk910/dynamic-ssz/sszutils"
@@ -16,7 +17,7 @@ import (
1617
type BuilderBid struct {
1718
Header *deneb.ExecutionPayloadHeader `json:"header"`
1819
BlobKZGCommitments []deneb.KZGCommitment `json:"blob_kzg_commitments"`
19-
ExecutionRequests *electra.ExecutionRequests `json:"execution_requests"`
20+
ExecutionRequests *eth2all.ExecutionRequests `json:"execution_requests"`
2021
Value *uint256.Int `json:"value"` // wei as uint256 in spec
2122
Pubkey phase0.BLSPubKey `json:"pubkey"`
2223
}
@@ -80,7 +81,7 @@ func (b *BuilderBid) HashTreeRootWith(hh sszutils.HashWalker) error {
8081
return err
8182
}
8283
} else {
83-
empty := &electra.ExecutionRequests{}
84+
empty := &eth2all.ExecutionRequests{Version: version.DataVersionFulu}
8485
if err := empty.HashTreeRootWith(hh); err != nil {
8586
return err
8687
}
@@ -116,7 +117,7 @@ type GetHeaderResponse struct {
116117
type builderBidJSON struct {
117118
Header *deneb.ExecutionPayloadHeader `json:"header"`
118119
BlobKZGCommitments []deneb.KZGCommitment `json:"blob_kzg_commitments"`
119-
ExecutionRequests *electra.ExecutionRequests `json:"execution_requests"`
120+
ExecutionRequests *eth2all.ExecutionRequests `json:"execution_requests"`
120121
Value string `json:"value"`
121122
Pubkey phase0.BLSPubKey `json:"pubkey"`
122123
}

pkg/builderapi/fulu/build.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
package fulu
33

44
import (
5-
"github.com/ethpandaops/go-eth2-client/spec/electra"
5+
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
66
"github.com/ethpandaops/go-eth2-client/spec/phase0"
7+
"github.com/ethpandaops/go-eth2-client/spec/version"
78
"github.com/holiman/uint256"
89

910
"github.com/ethpandaops/buildoor/pkg/payload_builder"
@@ -36,9 +37,13 @@ func BuildSignedBuilderBid(
3637
return nil, err
3738
}
3839

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

4449
value, overflow := uint256.FromBig(event.BlockValue)

pkg/payload_bidder/bid.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
77
"github.com/ethpandaops/go-eth2-client/spec/bellatrix"
88
"github.com/ethpandaops/go-eth2-client/spec/deneb"
9-
"github.com/ethpandaops/go-eth2-client/spec/electra"
109
"github.com/ethpandaops/go-eth2-client/spec/gloas"
1110
"github.com/ethpandaops/go-eth2-client/spec/phase0"
1211
dynssz "github.com/pk910/dynamic-ssz"
@@ -38,7 +37,7 @@ func BuildSignedBid(
3837
) (*eth2all.SignedExecutionPayloadBid, error) {
3938
execRequests := p.ExecutionRequests
4039
if execRequests == nil {
41-
execRequests = &electra.ExecutionRequests{}
40+
execRequests = &eth2all.ExecutionRequests{Version: p.ExecutionPayload.Version}
4241
}
4342

4443
// dynssz so preset-dependent list limits resolve from the global spec.

pkg/payload_bidder/reveal.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55

66
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
7-
"github.com/ethpandaops/go-eth2-client/spec/electra"
87
"github.com/ethpandaops/go-eth2-client/spec/gloas"
98
"github.com/ethpandaops/go-eth2-client/spec/phase0"
109

@@ -33,11 +32,7 @@ func BuildSignedEnvelope(
3332
) (signed *eth2all.SignedExecutionPayloadEnvelope, blobs, proofs [][]byte, err error) {
3433
execRequests := p.ExecutionRequests
3534
if execRequests == nil {
36-
execRequests = &electra.ExecutionRequests{
37-
Deposits: []*electra.DepositRequest{},
38-
Withdrawals: []*electra.WithdrawalRequest{},
39-
Consolidations: []*electra.ConsolidationRequest{},
40-
}
35+
execRequests = &eth2all.ExecutionRequests{Version: p.ExecutionPayload.Version}
4136
}
4237

4338
envelope := &eth2all.ExecutionPayloadEnvelope{

pkg/payload_builder/execution_requests.go

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,36 @@ import (
77
"github.com/ethpandaops/go-eth-engine-client/spec/prague"
88
"github.com/ethpandaops/go-eth2-client/spec/bellatrix"
99
"github.com/ethpandaops/go-eth2-client/spec/electra"
10+
"github.com/ethpandaops/go-eth2-client/spec/gloas"
1011
"github.com/ethpandaops/go-eth2-client/spec/phase0"
12+
"github.com/ethpandaops/go-eth2-client/spec/version"
13+
14+
eth2all "github.com/ethpandaops/go-eth2-client/spec/all"
1115
)
1216

1317
const (
14-
depositRequestType = 0x00
15-
withdrawalRequestType = 0x01
16-
consolidationRequestType = 0x02
17-
18-
depositRequestSize = 192 // 48 + 32 + 8 + 96 + 8
19-
withdrawalRequestSize = 76 // 20 + 48 + 8
20-
consolidationRequestSize = 116 // 20 + 48 + 48
18+
depositRequestType = 0x00
19+
withdrawalRequestType = 0x01
20+
consolidationRequestType = 0x02
21+
builderDepositRequestType = 0x03 // EIP-8282, Gloas+
22+
builderExitRequestType = 0x04 // EIP-8282, Gloas+
23+
24+
depositRequestSize = 192 // 48 + 32 + 8 + 96 + 8
25+
withdrawalRequestSize = 76 // 20 + 48 + 8
26+
consolidationRequestSize = 116 // 20 + 48 + 48
27+
builderDepositRequestSize = 184 // 48 + 32 + 8 + 96
28+
builderExitRequestSize = 68 // 20 + 48
2129
)
2230

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

81+
case builderDepositRequestType:
82+
if dataVersion < version.DataVersionGloas {
83+
return nil, fmt.Errorf("execution request %d: builder deposit request not valid before Gloas", i)
84+
}
85+
builderDeposits, err := parseBuilderDepositRequests(data)
86+
if err != nil {
87+
return nil, fmt.Errorf("execution request %d: %w", i, err)
88+
}
89+
result.BuilderDeposits = builderDeposits
90+
91+
case builderExitRequestType:
92+
if dataVersion < version.DataVersionGloas {
93+
return nil, fmt.Errorf("execution request %d: builder exit request not valid before Gloas", i)
94+
}
95+
builderExits, err := parseBuilderExitRequests(data)
96+
if err != nil {
97+
return nil, fmt.Errorf("execution request %d: %w", i, err)
98+
}
99+
result.BuilderExits = builderExits
100+
71101
default:
72102
return nil, fmt.Errorf("execution request %d: unknown type 0x%02x", i, reqType)
73103
}
@@ -170,3 +200,62 @@ func parseConsolidationRequests(data []byte) ([]*electra.ConsolidationRequest, e
170200

171201
return consolidations, nil
172202
}
203+
204+
func parseBuilderDepositRequests(data []byte) ([]*gloas.BuilderDepositRequest, error) {
205+
if len(data)%builderDepositRequestSize != 0 {
206+
return nil, fmt.Errorf("builder deposit requests: length %d not divisible by %d", len(data), builderDepositRequestSize)
207+
}
208+
209+
count := len(data) / builderDepositRequestSize
210+
deposits := make([]*gloas.BuilderDepositRequest, count)
211+
212+
for i := range count {
213+
d := data[i*builderDepositRequestSize : (i+1)*builderDepositRequestSize]
214+
215+
var pubkey phase0.BLSPubKey
216+
copy(pubkey[:], d[0:48])
217+
218+
withdrawalCreds := make([]byte, 32)
219+
copy(withdrawalCreds, d[48:80])
220+
221+
amount := phase0.Gwei(binary.LittleEndian.Uint64(d[80:88]))
222+
223+
var sig phase0.BLSSignature
224+
copy(sig[:], d[88:184])
225+
226+
deposits[i] = &gloas.BuilderDepositRequest{
227+
Pubkey: pubkey,
228+
WithdrawalCredentials: withdrawalCreds,
229+
Amount: amount,
230+
Signature: sig,
231+
}
232+
}
233+
234+
return deposits, nil
235+
}
236+
237+
func parseBuilderExitRequests(data []byte) ([]*gloas.BuilderExitRequest, error) {
238+
if len(data)%builderExitRequestSize != 0 {
239+
return nil, fmt.Errorf("builder exit requests: length %d not divisible by %d", len(data), builderExitRequestSize)
240+
}
241+
242+
count := len(data) / builderExitRequestSize
243+
exits := make([]*gloas.BuilderExitRequest, count)
244+
245+
for i := range count {
246+
d := data[i*builderExitRequestSize : (i+1)*builderExitRequestSize]
247+
248+
var addr bellatrix.ExecutionAddress
249+
copy(addr[:], d[0:20])
250+
251+
var pubkey phase0.BLSPubKey
252+
copy(pubkey[:], d[20:68])
253+
254+
exits[i] = &gloas.BuilderExitRequest{
255+
SourceAddress: addr,
256+
Pubkey: pubkey,
257+
}
258+
}
259+
260+
return exits, nil
261+
}

0 commit comments

Comments
 (0)