Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support external mev builder #10492

Merged
merged 83 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
3efc0ea
builder client
domiwei May 27, 2024
6bea85c
update
domiwei May 27, 2024
8eba4f9
builder option
domiwei May 28, 2024
eebb8a2
validator register
domiwei May 28, 2024
fefed02
part of block produce api
domiwei May 30, 2024
f9ac864
post blinded block
domiwei Jun 2, 2024
865f0d8
update
domiwei Jun 2, 2024
1694221
refine
domiwei Jun 3, 2024
8501d25
interface builder
domiwei Jun 3, 2024
0bef820
tweak api
domiwei Jun 3, 2024
d0caed6
update
domiwei Jun 4, 2024
29118d2
update
domiwei Jun 5, 2024
e7c6532
update
domiwei Jun 6, 2024
a077b7d
check builder header
domiwei Jun 6, 2024
768c618
more check
domiwei Jun 6, 2024
614967a
add caplin.mev-relay-url flag
domiwei Jun 6, 2024
6f0598e
udpate
domiwei Jun 7, 2024
2009fd3
log
domiwei Jun 7, 2024
4f5e6f7
fix
domiwei Jun 7, 2024
591587d
log
domiwei Jun 7, 2024
77cab7d
log
domiwei Jun 7, 2024
7773050
update
domiwei Jun 7, 2024
9f62895
log
domiwei Jun 7, 2024
0fb7aa0
update
domiwei Jun 10, 2024
4d9e014
update
domiwei Jun 10, 2024
6608085
update
domiwei Jun 10, 2024
6226664
update
domiwei Jun 10, 2024
a8f4458
update
domiwei Jun 10, 2024
9895ea7
update
domiwei Jun 10, 2024
5c87063
update
domiwei Jun 10, 2024
98de3e1
update
domiwei Jun 10, 2024
30c2989
update
domiwei Jun 11, 2024
644fbea
update
domiwei Jun 11, 2024
6b2ef3f
update
domiwei Jun 11, 2024
cc33490
update
domiwei Jun 11, 2024
9963f4e
fix header value
domiwei Jun 11, 2024
744b108
update
domiwei Jun 11, 2024
76e2f66
update
domiwei Jun 11, 2024
ddc6227
update
domiwei Jun 11, 2024
d58e120
update
domiwei Jun 11, 2024
ebdd24c
update
domiwei Jun 11, 2024
d59fcb1
update
domiwei Jun 11, 2024
2ccf7f9
try bls_execution_changes
domiwei Jun 11, 2024
fb7897d
update
domiwei Jun 11, 2024
7a25076
update
domiwei Jun 11, 2024
79459bc
update
domiwei Jun 11, 2024
d1dbdb0
remove omitempty
domiwei Jun 11, 2024
4668158
more logs
domiwei Jun 11, 2024
99d02bb
more logs
domiwei Jun 11, 2024
ffdea5d
try
domiwei Jun 11, 2024
61cd8bd
logs
domiwei Jun 11, 2024
1dfc97f
process blinded block
domiwei Jun 12, 2024
423a264
remove version
domiwei Jun 12, 2024
dc6b7ea
fix
domiwei Jun 12, 2024
77ba5a7
expected withdrawals
domiwei Jun 12, 2024
c11502c
update
domiwei Jun 12, 2024
9cf6ea5
update
domiwei Jun 13, 2024
3672e96
update
domiwei Jun 13, 2024
6769c79
update
domiwei Jun 13, 2024
0d898a8
update
domiwei Jun 13, 2024
4ca1f5a
header version
domiwei Jun 13, 2024
50a9ee8
update
domiwei Jun 13, 2024
4468332
update
domiwei Jun 13, 2024
05825a0
test
domiwei Jun 13, 2024
3522ce1
remove logs
domiwei Jun 13, 2024
e38c36c
update
domiwei Jun 14, 2024
d7b9d07
experiemnt
domiwei Jun 15, 2024
9e9872b
update
domiwei Jun 17, 2024
3fea028
update
domiwei Jun 17, 2024
e6290f1
experiment
domiwei Jun 18, 2024
9a8657f
done
domiwei Jun 18, 2024
65e3db6
panic fix
domiwei Jun 18, 2024
dd5131e
panic fix
domiwei Jun 18, 2024
b582007
panic fix
domiwei Jun 18, 2024
fdb8bcb
fix error
domiwei Jun 18, 2024
42c85ca
fix and clean up
domiwei Jun 20, 2024
c68d10f
new log pkg path
domiwei Jun 20, 2024
dac67c1
fix test
domiwei Jun 20, 2024
c299a33
fix test
domiwei Jun 20, 2024
a229d53
unittest for builder client
domiwei Jun 24, 2024
d0027ec
tweak
domiwei Jun 24, 2024
1179616
fix
domiwei Jun 24, 2024
115c502
update
domiwei Jun 24, 2024
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
194 changes: 194 additions & 0 deletions cl/beacon/builder/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package builder
domiwei marked this conversation as resolved.
Show resolved Hide resolved

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"

"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/log/v3"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/turbo/engineapi/engine_types"
)

var _ BuilderClient = &builderClient{}

var (
ErrNoContent = fmt.Errorf("no http content")
)

type builderClient struct {
// ref: https://ethereum.github.io/builder-specs/#/
httpClient *http.Client
url *url.URL
beaconConfig *clparams.BeaconChainConfig
}

func NewBlockBuilderClient(baseUrl string, beaconConfig *clparams.BeaconChainConfig) *builderClient {
u, err := url.Parse(baseUrl)
if err != nil {
panic(err)
}
c := &builderClient{
httpClient: &http.Client{},
url: u,
beaconConfig: beaconConfig,
}
if err := c.GetStatus(context.Background()); err != nil {
log.Error("cannot connect to builder client", "url", baseUrl, "error", err)
panic("cannot connect to builder client")
}
log.Info("Builder client is ready", "url", baseUrl)
return c
}

func (b *builderClient) RegisterValidator(ctx context.Context, registers []*cltypes.ValidatorRegistration) error {
// https://ethereum.github.io/builder-specs/#/Builder/registerValidator
path := "/eth/v1/builder/validators"
url := b.url.JoinPath(path).String()
if len(registers) == 0 {
return errors.New("empty registers")
}
payload, err := json.Marshal(registers)
if err != nil {
return err
}
_, err = httpCall[json.RawMessage](ctx, b.httpClient, http.MethodPost, url, nil, bytes.NewBuffer(payload))
if err == ErrNoContent {
// no content is ok
return nil
}
if err != nil {
log.Warn("[mev builder] httpCall error on RegisterValidator", "err", err)
}
return err
}

func (b *builderClient) GetHeader(ctx context.Context, slot int64, parentHash common.Hash, pubKey common.Bytes48) (*ExecutionHeader, error) {
// https://ethereum.github.io/builder-specs/#/Builder/getHeader
path := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", slot, parentHash.Hex(), pubKey.Hex())
url := b.url.JoinPath(path).String()
header, err := httpCall[ExecutionHeader](ctx, b.httpClient, http.MethodGet, url, nil, nil)
if err != nil {
log.Warn("[mev builder] httpCall error on GetExecutionPayloadHeader", "err", err, "slot", slot, "parentHash", parentHash.Hex(), "pubKey", pubKey.Hex())
return nil, err
}
return header, nil
}

func (b *builderClient) SubmitBlindedBlocks(ctx context.Context, block *cltypes.SignedBlindedBeaconBlock) (*cltypes.Eth1Block, *engine_types.BlobsBundleV1, error) {
// https://ethereum.github.io/builder-specs/#/Builder/submitBlindedBlocks
path := "/eth/v1/builder/blinded_blocks"
url := b.url.JoinPath(path).String()
payload, err := json.Marshal(block)
if err != nil {
return nil, nil, err
}
headers := map[string]string{
"Eth-Consensus-Version": block.Version().String(),
}
resp, err := httpCall[BlindedBlockResponse](ctx, b.httpClient, http.MethodPost, url, headers, bytes.NewBuffer(payload))
if err != nil {
log.Warn("[mev builder] httpCall error on SubmitBlindedBlocks", "err", err, "slot", block.Block.Slot)
return nil, nil, err
}

var eth1Block *cltypes.Eth1Block
var blobsBundle *engine_types.BlobsBundleV1
switch resp.Version {
case "bellatrix", "capella":
eth1Block = &cltypes.Eth1Block{}
if err := json.Unmarshal(resp.Data, block); err != nil {
return nil, nil, err
}
case "deneb":
denebResp := &struct {
ExecutionPayload *cltypes.Eth1Block `json:"execution_payload"`
BlobsBundle *engine_types.BlobsBundleV1 `json:"blobs_bundle"`
}{
ExecutionPayload: cltypes.NewEth1Block(clparams.DenebVersion, b.beaconConfig),
BlobsBundle: &engine_types.BlobsBundleV1{},
}
if err := json.Unmarshal(resp.Data, denebResp); err != nil {
return nil, nil, err
}
eth1Block = denebResp.ExecutionPayload
blobsBundle = denebResp.BlobsBundle
}
return eth1Block, blobsBundle, nil
}

func (b *builderClient) GetStatus(ctx context.Context) error {
path := "/eth/v1/builder/status"
url := b.url.JoinPath(path).String()
_, err := httpCall[json.RawMessage](ctx, b.httpClient, http.MethodGet, url, nil, nil)
if err == ErrNoContent {
// no content is ok, we just need to check if the server is up
return nil
}
return err
}

func httpCall[T any](ctx context.Context, client *http.Client, method, url string, headers map[string]string, payloadReader io.Reader) (*T, error) {
request, err := http.NewRequestWithContext(ctx, method, url, payloadReader)
if err != nil {
log.Warn("[mev builder] http.NewRequest failed", "err", err, "url", url, "method", method)
return nil, err
}
request.Header.Set("Content-Type", "application/json")
for k, v := range headers {
request.Header.Set(k, v)
}
// send request
response, err := client.Do(request)
if err != nil {
log.Warn("[mev builder] client.Do failed", "err", err, "url", url, "method", method)
return nil, err
}
defer func() {
if response.Body != nil {
response.Body.Close()
}
}()
if response.StatusCode < 200 || response.StatusCode > 299 {
// read response body
if response.Body == nil {
return nil, fmt.Errorf("status code: %d", response.StatusCode)
}
bytes, err := io.ReadAll(response.Body)
if err != nil {
log.Warn("[mev builder] io.ReadAll failed", "err", err, "url", url, "method", method)
} else {
log.Debug("[mev builder] httpCall failed", "status", response.Status, "content", string(bytes))
}
return nil, fmt.Errorf("status code: %d", response.StatusCode)
}
if response.StatusCode == http.StatusNoContent {
return nil, ErrNoContent
}

// read response body
var body T
if response.Body == nil {
return &body, nil
}
bytes, err := io.ReadAll(response.Body)
if err != nil {
log.Warn("[mev builder] io.ReadAll failed", "err", err, "url", url, "method", method)
return nil, err
}
if len(bytes) == 0 {
return &body, nil
}
if err := json.Unmarshal(bytes, &body); err != nil {
log.Warn("[mev builder] json.Unmarshal error", "err", err, "content", string(bytes))
return nil, err
}
return &body, nil
}
Loading
Loading