From c3bc2ec930383044c55174f4b3ef62935ed2d98f Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 4 Sep 2024 15:03:14 +1000 Subject: [PATCH] Add proposer authentication to builder payload --- builder/README.md | 4 +- builder/beacon_client.go | 9 +- builder/beacon_client_test.go | 11 +- builder/builder.go | 419 ++++++---------------- builder/builder_test.go | 90 ++--- builder/config.go | 5 +- builder/eth_service.go | 5 +- builder/eth_service_test.go | 3 +- builder/service.go | 58 ++- builder/{types.go => types/attributes.go} | 11 +- builder/types/payload.go | 254 +++++++++++++ builder/types/payload_test.go | 133 +++++++ builder/types/version.go | 58 +++ builder/utils.go | 25 ++ cmd/geth/main.go | 4 +- cmd/utils/flags.go | 24 +- go.mod | 7 +- go.sum | 24 -- 18 files changed, 691 insertions(+), 453 deletions(-) rename builder/{types.go => types/attributes.go} (85%) create mode 100644 builder/types/payload.go create mode 100644 builder/types/payload_test.go create mode 100644 builder/types/version.go diff --git a/builder/README.md b/builder/README.md index 2d4940c1d5..0d6fa97c32 100644 --- a/builder/README.md +++ b/builder/README.md @@ -21,7 +21,7 @@ sequenceDiagram OPB-->>BB: PayloadAttributes Note right of BB: timespan for building blocks - OPS->> BB: /eth/v1/builder/payload/{slot}/{parent_hash} + OPS->> BB: POST /eth/v1/builder/payload BB-->>OPS: BuilderPayload OPS->> EES: engine_getPayload OPS-->>OPS: SimulatePayload @@ -43,5 +43,7 @@ To enable the builder: * `--builder` Enable the Builder module * `--builder.beacon_endpoints` list of op-node SSE event stream endpoints to subscribe from + * `--builder.signing_key` private key to sign requested block payloads + * `--builder.proposer_signing_address` signing address used to authenticate the proposer Run `geth --help` for the full list of builder configurations. \ No newline at end of file diff --git a/builder/beacon_client.go b/builder/beacon_client.go index 16f2ac415e..e031509983 100644 --- a/builder/beacon_client.go +++ b/builder/beacon_client.go @@ -6,19 +6,20 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/builder/types" "github.com/ethereum/go-ethereum/log" "github.com/r3labs/sse" ) type IBeaconClient interface { - SubscribeToPayloadAttributesEvents(payloadAttrC chan BuilderPayloadAttributes) + SubscribeToPayloadAttributesEvents(payloadAttrC chan types.PayloadAttributes) Start() error Stop() } type NilBeaconClient struct{} -func (b *NilBeaconClient) SubscribeToPayloadAttributesEvents(payloadAttrC chan BuilderPayloadAttributes) { +func (b *NilBeaconClient) SubscribeToPayloadAttributesEvents(payloadAttrC chan types.PayloadAttributes) { } func (b *NilBeaconClient) Start() error { return nil } @@ -43,14 +44,14 @@ func NewOpBeaconClient(endpoint string) *OpBeaconClient { } } -func (opbc *OpBeaconClient) SubscribeToPayloadAttributesEvents(payloadAttrC chan BuilderPayloadAttributes) { +func (opbc *OpBeaconClient) SubscribeToPayloadAttributesEvents(payloadAttrC chan types.PayloadAttributes) { eventsURL := fmt.Sprintf("%s/events", opbc.endpoint) log.Info("subscribing to payload_attributes events", "url", eventsURL) for { client := sse.NewClient(eventsURL) err := client.SubscribeWithContext(opbc.ctx, "payload_attributes", func(msg *sse.Event) { - data := new(BuilderPayloadAttributes) + data := new(types.PayloadAttributes) err := json.Unmarshal(msg.Data, data) if err != nil { log.Error("could not unmarshal payload_attributes event", "err", err) diff --git a/builder/beacon_client_test.go b/builder/beacon_client_test.go index 7407f3edca..4803e78414 100644 --- a/builder/beacon_client_test.go +++ b/builder/beacon_client_test.go @@ -4,16 +4,17 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/builder/types" "gotest.tools/assert" ) type mockBeaconNode struct { - payloadAttributes BuilderPayloadAttributes + payloadAttributes types.PayloadAttributes } func (b *mockBeaconNode) Stop() {} -func (b *mockBeaconNode) SubscribeToPayloadAttributesEvents(payloadAttrC chan BuilderPayloadAttributes) { +func (b *mockBeaconNode) SubscribeToPayloadAttributesEvents(payloadAttrC chan types.PayloadAttributes) { go func() { payloadAttrC <- b.payloadAttributes }() @@ -21,15 +22,15 @@ func (b *mockBeaconNode) SubscribeToPayloadAttributesEvents(payloadAttrC chan Bu func (b *mockBeaconNode) Start() error { return nil } -func newMockBeaconNode(payloadAttributes BuilderPayloadAttributes) *mockBeaconNode { +func newMockBeaconNode(payloadAttributes types.PayloadAttributes) *mockBeaconNode { return &mockBeaconNode{ payloadAttributes: payloadAttributes, } } func TestMockBeaconNode(t *testing.T) { - mockBeaconNode := newMockBeaconNode(BuilderPayloadAttributes{Slot: 123}) - payloadAttrC := make(chan BuilderPayloadAttributes, 1) // Use buffered channel + mockBeaconNode := newMockBeaconNode(types.PayloadAttributes{Slot: 123}) + payloadAttrC := make(chan types.PayloadAttributes, 1) // Use buffered channel mockBeaconNode.SubscribeToPayloadAttributesEvents(payloadAttrC) diff --git a/builder/builder.go b/builder/builder.go index c6ce53d540..da75f242c6 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -1,37 +1,26 @@ package builder import ( + "bytes" "context" + "crypto/ecdsa" "encoding/json" "errors" "fmt" + "io" "math/big" "net/http" _ "os" - "strconv" + "strings" "sync" "time" - builderApi "github.com/attestantio/go-builder-client/api" - builderApiBellatrix "github.com/attestantio/go-builder-client/api/bellatrix" - builderApiCapella "github.com/attestantio/go-builder-client/api/capella" - builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" - builderApiV1 "github.com/attestantio/go-builder-client/api/v1" - builderSpec "github.com/attestantio/go-builder-client/spec" - "github.com/attestantio/go-eth2-client/spec" - "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/attestantio/go-eth2-client/spec/capella" - "github.com/attestantio/go-eth2-client/spec/deneb" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/beacon/engine" + builderTypes "github.com/ethereum/go-ethereum/builder/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/flashbots/go-boost-utils/bls" - "github.com/flashbots/go-boost-utils/ssz" - boostTypes "github.com/flashbots/go-boost-utils/types" - "github.com/flashbots/go-boost-utils/utils" - "github.com/gorilla/mux" + "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" ) @@ -45,7 +34,7 @@ var ( ) type IBuilder interface { - GetPayload(request PayloadRequestV1) (*builderSpec.VersionedSubmitBlockRequest, error) + GetPayload(request *builderTypes.BuilderPayloadRequest) (*builderTypes.VersionedBuilderPayloadResponse, error) Start() error Stop() error @@ -56,28 +45,30 @@ type Builder struct { eth IEthereumService ignoreLatePayloadAttributes bool beaconClient IBeaconClient - builderSecretKey *bls.SecretKey - builderPublicKey phase0.BLSPubKey - builderSigningDomain phase0.Domain + builderPrivateKey *ecdsa.PrivateKey + builderAddress common.Address builderRetryInterval time.Duration builderBlockTime time.Duration + proposerAddress common.Address + slotMu sync.Mutex - slotAttrs BuilderPayloadAttributes + slotAttrs builderTypes.PayloadAttributes slotCtx context.Context slotCtxCancel context.CancelFunc bestBlockMu sync.Mutex - bestBlock *builderSpec.VersionedSubmitBlockRequest + bestBlock *builderTypes.VersionedBuilderPayloadResponse stop chan struct{} } // BuilderArgs is a struct that contains all the arguments needed to create a new Builder type BuilderArgs struct { - sk *bls.SecretKey - builderSigningDomain phase0.Domain + builderPrivateKey *ecdsa.PrivateKey + builderAddress common.Address + proposerAddress common.Address builderRetryInterval time.Duration blockTime time.Duration eth IEthereumService @@ -91,30 +82,19 @@ type SubmitBlockOpts struct { ExecutionPayloadEnvelope *engine.ExecutionPayloadEnvelope // SealedAt is the time at which the block was sealed SealedAt time.Time - // ProposerPubkey is the proposer's pubkey - ProposerPubkey phase0.BLSPubKey // PayloadAttributes are the payload attributes used for block building - PayloadAttributes *BuilderPayloadAttributes + PayloadAttributes *builderTypes.PayloadAttributes } func NewBuilder(args BuilderArgs) (*Builder, error) { - blsPk, err := bls.PublicKeyFromSecretKey(args.sk) - if err != nil { - return nil, err - } - pk, err := utils.BlsPublicKeyToPublicKey(blsPk) - if err != nil { - return nil, err - } - slotCtx, slotCtxCancel := context.WithCancel(context.Background()) return &Builder{ eth: args.eth, ignoreLatePayloadAttributes: args.ignoreLatePayloadAttributes, beaconClient: args.beaconClient, - builderSecretKey: args.sk, - builderPublicKey: pk, - builderSigningDomain: args.builderSigningDomain, + builderPrivateKey: args.builderPrivateKey, + builderAddress: args.builderAddress, + proposerAddress: args.proposerAddress, builderRetryInterval: args.builderRetryInterval, builderBlockTime: args.blockTime, @@ -129,7 +109,7 @@ func (b *Builder) Start() error { log.Info("Starting builder") // Start regular payload attributes updates go func() { - c := make(chan BuilderPayloadAttributes) + c := make(chan builderTypes.PayloadAttributes) go b.beaconClient.SubscribeToPayloadAttributesEvents(c) currentSlot := uint64(0) @@ -178,8 +158,22 @@ func (b *Builder) Stop() error { return nil } -func (b *Builder) GetPayload(request PayloadRequestV1) (*builderSpec.VersionedSubmitBlockRequest, error) { - log.Info("received get payload request", "slot", request.Slot, "parent", request.ParentHash) +func (b *Builder) GetPayload(request *builderTypes.BuilderPayloadRequest) (*builderTypes.VersionedBuilderPayloadResponse, error) { + if request == nil { + return nil, errors.New("request is nil") + } + + log.Info("received get payload request", "slot", request.Message.Slot, "parent", request.Message.ParentHash) + + // verify proposer signature + if b.proposerAddress != (common.Address{}) { + err := b.verifyProposerSignature(request) + if err != nil { + log.Warn("proposer signature verification failed", "err", err) + return nil, fmt.Errorf("proposer signature verification failed: %w", err) + } + } + b.bestBlockMu.Lock() bestBlock := b.bestBlock b.bestBlockMu.Unlock() @@ -189,55 +183,75 @@ func (b *Builder) GetPayload(request PayloadRequestV1) (*builderSpec.VersionedSu return nil, ErrNoPayloads } - submittedSlot, err := bestBlock.Slot() - if err != nil { - log.Error("could not get slot from best submission", "err", err) - return nil, ErrSlotFromPayload + if bestBlock.Message.Slot != request.Message.Slot { + log.Warn("slot not equal", "requested", request.Message.Slot, "block", bestBlock.Message.Slot) + return nil, ErrSlotMismatch } - if submittedSlot != uint64(request.Slot) { - log.Warn("slot not equal", "requested", request.Slot, "block", submittedSlot) - return nil, ErrSlotMismatch + if bestBlock.Message.ParentHash != request.Message.ParentHash { + log.Warn("parent hash not equal", "requested", request.Message.ParentHash, "block", bestBlock.Message.ParentHash.String()) + return nil, ErrParentHashMismatch } - submittedParentHash, err := bestBlock.ParentHash() + log.Info("payload delivered", "hash", bestBlock.Message.BlockHash.String()) + + return bestBlock, nil +} + +func (b *Builder) verifyProposerSignature(payload *builderTypes.BuilderPayloadRequest) error { + msg, err := rlp.EncodeToBytes(payload.Message) if err != nil { - log.Error("could not get parent hash from best submission", "err", err) - return nil, ErrParentHashFromPayload + return fmt.Errorf("could not encode message, %w", err) } - - if submittedParentHash.String() != request.ParentHash.String() { - log.Warn("parent hash not equal", "requested", request.ParentHash, "block", submittedParentHash.String()) - return nil, ErrParentHashMismatch + signingHash, err := BuilderSigningHash(b.eth.Config(), msg) + if err != nil { + return fmt.Errorf("could not get signing hash, %w", err) } - - blockHash, err := bestBlock.BlockHash() + recoveredPubkey, err := crypto.SigToPub(signingHash[:], payload.Signature) if err != nil { - log.Warn("could not get block hash from best submission", "err", err) - } else { - log.Info("payload delivered", "hash", blockHash.String()) + return fmt.Errorf("could not recover pubkey, %w", err) } - - return bestBlock, nil + recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) + if recoveredAddress != b.proposerAddress { + return fmt.Errorf("recovered address does not match proposer address, %s != %s", recoveredAddress, b.proposerAddress) + } + return nil } func (b *Builder) handleGetPayload(w http.ResponseWriter, req *http.Request) { start := time.Now() - vars := mux.Vars(req) - slot, err := strconv.Atoi(vars["slot"]) + success := false + + defer func() { + // Collect metrics at end of request + updateServeTimeHistogram("getPayload", success, time.Since(start)) + }() + + // Read the body first, so we can decode it later + body, err := io.ReadAll(req.Body) if err != nil { - updateServeTimeHistogram("getPayload", false, time.Since(start)) - respondError(w, http.StatusBadRequest, "incorrect slot") + if strings.Contains(err.Error(), "i/o timeout") { + log.Error("getPayload request failed to decode (i/o timeout)", "err", err) + respondError(w, http.StatusInternalServerError, err.Error()) + return + } + + log.Error("could not read body of request from the op node", "err", err) + respondError(w, http.StatusBadRequest, err.Error()) return } - parentHashHex := vars["parent_hash"] - log.Info("received handle get payload request", "slot", slot, "parent", parentHashHex) + // Decode payload + payload := new(builderTypes.BuilderPayloadRequest) + if err := json.NewDecoder(bytes.NewReader(body)).Decode(payload); err != nil { + log.Warn("failed to decode getPayload request", "err", err) + respondError(w, http.StatusBadRequest, "failed to decode payload") + return + } - bestSubmission, err := b.GetPayload(PayloadRequestV1{ - Slot: uint64(slot), - ParentHash: common.HexToHash(parentHashHex), - }) + log.Info("received handle get payload request", "slot", payload.Message.Slot, "parent", payload.Message.ParentHash.String()) + + bestSubmission, err := b.GetPayload(payload) if err != nil { handleError(w, err) updateServeTimeHistogram("getPayload", false, time.Since(start)) @@ -264,13 +278,13 @@ func (b *Builder) saveBlockSubmission(opts SubmitBlockOpts) error { "hash", executionPayload.BlockHash.String(), ) - var dataVersion spec.DataVersion + var dataVersion builderTypes.SpecVersion if b.eth.Config().IsEcotone(executionPayload.Timestamp) { - dataVersion = spec.DataVersionDeneb + dataVersion = builderTypes.SpecVersionBedrock } else if b.eth.Config().IsCanyon(executionPayload.Timestamp) { - dataVersion = spec.DataVersionCapella + dataVersion = builderTypes.SpecVersionCanyon } else { - dataVersion = spec.DataVersionBellatrix + dataVersion = builderTypes.SpecVersionEcotone } value, overflow := uint256.FromBig(opts.ExecutionPayloadEnvelope.BlockValue) @@ -278,22 +292,28 @@ func (b *Builder) saveBlockSubmission(opts SubmitBlockOpts) error { return fmt.Errorf("could not set block value due to value overflow") } - blockBidMsg := builderApiV1.BidTrace{ + blockBidMsg := builderTypes.BidTrace{ Slot: opts.PayloadAttributes.Slot, - ParentHash: phase0.Hash32(executionPayload.ParentHash), - BlockHash: phase0.Hash32(executionPayload.BlockHash), - BuilderPubkey: b.builderPublicKey, - ProposerPubkey: opts.ProposerPubkey, - ProposerFeeRecipient: bellatrix.ExecutionAddress(opts.PayloadAttributes.SuggestedFeeRecipient), + ParentHash: executionPayload.ParentHash, + BlockHash: executionPayload.BlockHash, + BuilderAddress: b.builderAddress, + ProposerAddress: b.proposerAddress, + ProposerFeeRecipient: opts.PayloadAttributes.SuggestedFeeRecipient, GasLimit: executionPayload.GasLimit, GasUsed: executionPayload.GasUsed, - Value: value, + Value: value.ToBig(), } - versionedBlockRequest, err := b.getBlockRequest(opts.ExecutionPayloadEnvelope, dataVersion, &blockBidMsg) + signature, err := b.signBuilderBid(&blockBidMsg) if err != nil { - log.Error("could not get block request", "err", err) - return err + return fmt.Errorf("could not sign block bid message, %w", err) + } + + versionedBlockRequest := &builderTypes.VersionedBuilderPayloadResponse{ + Version: dataVersion, + Message: &blockBidMsg, + ExecutionPayload: executionPayload, + Signature: signature, } b.bestBlockMu.Lock() @@ -306,57 +326,22 @@ func (b *Builder) saveBlockSubmission(opts SubmitBlockOpts) error { return nil } -func (b *Builder) getBlockRequest(executableData *engine.ExecutionPayloadEnvelope, dataVersion spec.DataVersion, blockBidMsg *builderApiV1.BidTrace) (*builderSpec.VersionedSubmitBlockRequest, error) { - payload, err := executableDataToExecutionPayload(executableData, dataVersion) +func (b *Builder) signBuilderBid(bid *builderTypes.BidTrace) ([]byte, error) { + bidBytes, err := rlp.EncodeToBytes(bid) if err != nil { - log.Error("could not format execution payload", "err", err) return nil, err } - signature, err := ssz.SignMessage(blockBidMsg, b.builderSigningDomain, b.builderSecretKey) + cfg := b.eth.Config() + hash, err := BuilderSigningHash(cfg, bidBytes) if err != nil { - log.Error("could not sign builder bid", "err", err) return nil, err } - var versionedBlockRequest builderSpec.VersionedSubmitBlockRequest - switch dataVersion { - case spec.DataVersionBellatrix: - blockSubmitReq := builderApiBellatrix.SubmitBlockRequest{ - Signature: signature, - Message: blockBidMsg, - ExecutionPayload: payload.Bellatrix, - } - versionedBlockRequest = builderSpec.VersionedSubmitBlockRequest{ - Version: spec.DataVersionBellatrix, - Bellatrix: &blockSubmitReq, - } - case spec.DataVersionCapella: - blockSubmitReq := builderApiCapella.SubmitBlockRequest{ - Signature: signature, - Message: blockBidMsg, - ExecutionPayload: payload.Capella, - } - versionedBlockRequest = builderSpec.VersionedSubmitBlockRequest{ - Version: spec.DataVersionCapella, - Capella: &blockSubmitReq, - } - case spec.DataVersionDeneb: - blockSubmitReq := builderApiDeneb.SubmitBlockRequest{ - Signature: signature, - Message: blockBidMsg, - ExecutionPayload: payload.Deneb.ExecutionPayload, - BlobsBundle: payload.Deneb.BlobsBundle, - } - versionedBlockRequest = builderSpec.VersionedSubmitBlockRequest{ - Version: spec.DataVersionDeneb, - Deneb: &blockSubmitReq, - } - } - return &versionedBlockRequest, err + return crypto.Sign(hash[:], b.builderPrivateKey) } -func (b *Builder) handlePayloadAttributes(attrs *BuilderPayloadAttributes) error { +func (b *Builder) handlePayloadAttributes(attrs *builderTypes.PayloadAttributes) error { if attrs == nil { return nil } @@ -368,8 +353,6 @@ func (b *Builder) handlePayloadAttributes(attrs *BuilderPayloadAttributes) error return fmt.Errorf("parent block hash not found in block tree given head block hash %s", attrs.HeadHash) } - proposerPubkey := phase0.BLSPubKey{} - if !b.eth.Synced() { return errors.New("backend not Synced") } @@ -391,11 +374,11 @@ func (b *Builder) handlePayloadAttributes(attrs *BuilderPayloadAttributes) error b.slotCtx = slotCtx b.slotCtxCancel = slotCtxCancel - go b.runBuildingJob(b.slotCtx, proposerPubkey, attrs) + go b.runBuildingJob(b.slotCtx, attrs) return nil } -func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0.BLSPubKey, attrs *BuilderPayloadAttributes) { +func (b *Builder) runBuildingJob(slotCtx context.Context, attrs *builderTypes.PayloadAttributes) { ctx, cancel := context.WithTimeout(slotCtx, b.builderBlockTime) defer cancel() @@ -433,7 +416,6 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. submitBlockOpts := SubmitBlockOpts{ ExecutionPayloadEnvelope: payload, SealedAt: sealedAt, - ProposerPubkey: proposerPubkey, PayloadAttributes: attrs, } err := b.saveBlockSubmission(submitBlockOpts) @@ -443,170 +425,3 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. } }) } - -func executableDataToExecutionPayload(data *engine.ExecutionPayloadEnvelope, version spec.DataVersion) (*builderApi.VersionedSubmitBlindedBlockResponse, error) { - // if version in phase0, altair, unsupported version - if version == spec.DataVersionUnknown || version == spec.DataVersionPhase0 || version == spec.DataVersionAltair { - return nil, fmt.Errorf("unsupported data version %d", version) - } - - payload := data.ExecutionPayload - blobsBundle := data.BlobsBundle - - transactionData := make([]bellatrix.Transaction, len(payload.Transactions)) - for i, tx := range payload.Transactions { - transactionData[i] = bellatrix.Transaction(tx) - } - - baseFeePerGas := new(boostTypes.U256Str) - err := baseFeePerGas.FromBig(payload.BaseFeePerGas) - if err != nil { - return nil, err - } - - if version == spec.DataVersionBellatrix { - return getBellatrixPayload(payload, *baseFeePerGas, transactionData), nil - } - - withdrawalData := make([]*capella.Withdrawal, len(payload.Withdrawals)) - for i, wd := range payload.Withdrawals { - withdrawalData[i] = &capella.Withdrawal{ - Index: capella.WithdrawalIndex(wd.Index), - ValidatorIndex: phase0.ValidatorIndex(wd.Validator), - Address: bellatrix.ExecutionAddress(wd.Address), - Amount: phase0.Gwei(wd.Amount), - } - } - if version == spec.DataVersionCapella { - return getCapellaPayload(payload, *baseFeePerGas, transactionData, withdrawalData), nil - } - - uint256BaseFeePerGas, overflow := uint256.FromBig(payload.BaseFeePerGas) - if overflow { - return nil, fmt.Errorf("base fee per gas overflow") - } - - if len(blobsBundle.Blobs) != len(blobsBundle.Commitments) || len(blobsBundle.Blobs) != len(blobsBundle.Proofs) { - return nil, fmt.Errorf("blobs bundle length mismatch") - } - - if version == spec.DataVersionDeneb { - return getDenebPayload(payload, uint256BaseFeePerGas, transactionData, withdrawalData, blobsBundle), nil - } - - return nil, fmt.Errorf("unsupported data version %d", version) -} - -func getBellatrixPayload( - payload *engine.ExecutableData, - baseFeePerGas [32]byte, - transactions []bellatrix.Transaction, -) *builderApi.VersionedSubmitBlindedBlockResponse { - return &builderApi.VersionedSubmitBlindedBlockResponse{ - Version: spec.DataVersionBellatrix, - Bellatrix: &bellatrix.ExecutionPayload{ - ParentHash: [32]byte(payload.ParentHash), - FeeRecipient: [20]byte(payload.FeeRecipient), - StateRoot: [32]byte(payload.StateRoot), - ReceiptsRoot: [32]byte(payload.ReceiptsRoot), - LogsBloom: types.BytesToBloom(payload.LogsBloom), - PrevRandao: [32]byte(payload.Random), - BlockNumber: payload.Number, - GasLimit: payload.GasLimit, - GasUsed: payload.GasUsed, - Timestamp: payload.Timestamp, - ExtraData: payload.ExtraData, - BaseFeePerGas: baseFeePerGas, - BlockHash: [32]byte(payload.BlockHash), - Transactions: transactions, - }, - } -} - -func getCapellaPayload( - payload *engine.ExecutableData, - baseFeePerGas [32]byte, - transactions []bellatrix.Transaction, - withdrawals []*capella.Withdrawal, -) *builderApi.VersionedSubmitBlindedBlockResponse { - return &builderApi.VersionedSubmitBlindedBlockResponse{ - Version: spec.DataVersionCapella, - Capella: &capella.ExecutionPayload{ - ParentHash: [32]byte(payload.ParentHash), - FeeRecipient: [20]byte(payload.FeeRecipient), - StateRoot: [32]byte(payload.StateRoot), - ReceiptsRoot: [32]byte(payload.ReceiptsRoot), - LogsBloom: types.BytesToBloom(payload.LogsBloom), - PrevRandao: [32]byte(payload.Random), - BlockNumber: payload.Number, - GasLimit: payload.GasLimit, - GasUsed: payload.GasUsed, - Timestamp: payload.Timestamp, - ExtraData: payload.ExtraData, - BaseFeePerGas: baseFeePerGas, - BlockHash: [32]byte(payload.BlockHash), - Transactions: transactions, - Withdrawals: withdrawals, - }, - } -} - -func getBlobsBundle(blobsBundle *engine.BlobsBundleV1) *builderApiDeneb.BlobsBundle { - commitments := make([]deneb.KZGCommitment, len(blobsBundle.Commitments)) - proofs := make([]deneb.KZGProof, len(blobsBundle.Proofs)) - blobs := make([]deneb.Blob, len(blobsBundle.Blobs)) - - // we assume the lengths for blobs bundle is validated beforehand to be the same - for i := range blobsBundle.Blobs { - var commitment deneb.KZGCommitment - copy(commitment[:], blobsBundle.Commitments[i][:]) - commitments[i] = commitment - - var proof deneb.KZGProof - copy(proof[:], blobsBundle.Proofs[i][:]) - proofs[i] = proof - - var blob deneb.Blob - copy(blob[:], blobsBundle.Blobs[i][:]) - blobs[i] = blob - } - return &builderApiDeneb.BlobsBundle{ - Commitments: commitments, - Proofs: proofs, - Blobs: blobs, - } -} - -func getDenebPayload( - payload *engine.ExecutableData, - baseFeePerGas *uint256.Int, - transactions []bellatrix.Transaction, - withdrawals []*capella.Withdrawal, - blobsBundle *engine.BlobsBundleV1, -) *builderApi.VersionedSubmitBlindedBlockResponse { - return &builderApi.VersionedSubmitBlindedBlockResponse{ - Version: spec.DataVersionDeneb, - Deneb: &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ - ExecutionPayload: &deneb.ExecutionPayload{ - ParentHash: [32]byte(payload.ParentHash), - FeeRecipient: [20]byte(payload.FeeRecipient), - StateRoot: [32]byte(payload.StateRoot), - ReceiptsRoot: [32]byte(payload.ReceiptsRoot), - LogsBloom: types.BytesToBloom(payload.LogsBloom), - PrevRandao: [32]byte(payload.Random), - BlockNumber: payload.Number, - GasLimit: payload.GasLimit, - GasUsed: payload.GasUsed, - Timestamp: payload.Timestamp, - ExtraData: payload.ExtraData, - BaseFeePerGas: baseFeePerGas, - BlockHash: [32]byte(payload.BlockHash), - Transactions: transactions, - Withdrawals: withdrawals, - BlobGasUsed: *payload.BlobGasUsed, - ExcessBlobGas: *payload.ExcessBlobGas, - }, - BlobsBundle: getBlobsBundle(blobsBundle), - }, - } -} diff --git a/builder/builder_test.go b/builder/builder_test.go index bdb57946c3..b1c651f73d 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -5,21 +5,15 @@ import ( "testing" "time" - builderApiV1 "github.com/attestantio/go-builder-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/attestantio/go-eth2-client/spec/capella" - "github.com/attestantio/go-eth2-client/spec/deneb" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/beacon/engine" + builderTypes "github.com/ethereum/go-ethereum/builder/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" - "github.com/flashbots/go-boost-utils/bls" - "github.com/flashbots/go-boost-utils/ssz" - "github.com/flashbots/go-boost-utils/utils" - "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/require" ) @@ -29,7 +23,7 @@ type testEthereumService struct { testBlock *types.Block } -func (t *testEthereumService) BuildBlock(attrs *BuilderPayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) { +func (t *testEthereumService) BuildBlock(attrs *builderTypes.PayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) { return t.testExecutableData, nil } @@ -50,17 +44,13 @@ func TestGetPayloadV1(t *testing.T) { validatorDesiredGasLimit = 30_000_000 payloadAttributeGasLimit = 0 parentBlockGasLimit = 29_000_000 + testPrivateKeyHex = "2fc12ae741f29701f8e30f5de6350766c020cb80768a0ff01e6838ffd2431e11" ) - expectedGasLimit := core.CalcGasLimit(parentBlockGasLimit, validatorDesiredGasLimit) - - feeRecipient, err := utils.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc00") - require.NoError(t, err) - sk, err := bls.SecretKeyFromBytes(hexutil.MustDecode("0x31ee185dad1220a8c88ca5275e64cf5a5cb09cb621cb30df52c9bee8fbaaf8d7")) + testPrivateKey, err := crypto.HexToECDSA(testPrivateKeyHex) require.NoError(t, err) - - bDomain := ssz.ComputeDomain(ssz.DomainTypeAppBuilder, [4]byte{0x02, 0x0, 0x0, 0x0}, phase0.Root{}) - + feeRecipient := common.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc00") + expectedGasLimit := core.CalcGasLimit(parentBlockGasLimit, validatorDesiredGasLimit) zero := uint64(0) blockHash := common.HexToHash("5fc0137650b887cdb47ae6426d5e0e368e315a2ad3f93df80a8d14f8e1ce239a") testExecutableData := &engine.ExecutionPayloadEnvelope{ @@ -91,7 +81,7 @@ func TestGetPayloadV1(t *testing.T) { testBlock, err := engine.ExecutableDataToBlock(*testExecutableData.ExecutionPayload, nil, nil) require.NoError(t, err) - testPayloadAttributes := &BuilderPayloadAttributes{ + testPayloadAttributes := &builderTypes.PayloadAttributes{ Timestamp: hexutil.Uint64(104), Random: common.Hash{0x05, 0x10}, SuggestedFeeRecipient: common.Address(feeRecipient), @@ -111,8 +101,9 @@ func TestGetPayloadV1(t *testing.T) { mockBeaconNode := newMockBeaconNode(*testPayloadAttributes) builderArgs := BuilderArgs{ - sk: sk, - builderSigningDomain: bDomain, + builderPrivateKey: testPrivateKey, + builderAddress: crypto.PubkeyToAddress(testPrivateKey.PublicKey), + proposerAddress: crypto.PubkeyToAddress(testPrivateKey.PublicKey), builderRetryInterval: 200 * time.Millisecond, blockTime: 2 * time.Second, eth: testEthService, @@ -125,50 +116,41 @@ func TestGetPayloadV1(t *testing.T) { defer builder.Stop() time.Sleep(2 * time.Second) + msg := builderTypes.PayloadRequestV1{ + Slot: testPayloadAttributes.Slot, + ParentHash: testPayloadAttributes.HeadHash, + } + requestBytes, err := rlp.EncodeToBytes(&msg) + require.NoError(t, err) + hash, err := BuilderSigningHash(builder.eth.Config(), requestBytes) + require.NoError(t, err) + signature, err := crypto.Sign(hash[:], testPrivateKey) + require.NoError(t, err) blockResponse, err := builder.GetPayload( - PayloadRequestV1{ - Slot: testPayloadAttributes.Slot, - ParentHash: testPayloadAttributes.HeadHash, + &builderTypes.BuilderPayloadRequest{ + Message: msg, + Signature: signature, }, ) require.NoError(t, err) - expectedMessage := &builderApiV1.BidTrace{ + expectedMessage := &builderTypes.BidTrace{ Slot: uint64(25), - BlockHash: phase0.Hash32(blockHash), - ParentHash: phase0.Hash32{0x02, 0x03}, - BuilderPubkey: builder.builderPublicKey, - ProposerPubkey: phase0.BLSPubKey{}, + BlockHash: blockHash, + ParentHash: common.Hash{0x02, 0x03}, + BuilderAddress: builderArgs.builderAddress, + ProposerAddress: builderArgs.proposerAddress, ProposerFeeRecipient: feeRecipient, GasLimit: expectedGasLimit, GasUsed: uint64(100), - Value: &uint256.Int{0x0a}, + Value: big.NewInt(10), } copy(expectedMessage.BlockHash[:], blockHash[:]) - require.NotNil(t, blockResponse.Deneb) - require.Equal(t, expectedMessage, blockResponse.Deneb.Message) - - expectedExecutionPayload := &deneb.ExecutionPayload{ - ParentHash: phase0.Hash32{0x02, 0x03}, - FeeRecipient: feeRecipient, - StateRoot: [32]byte(testExecutableData.ExecutionPayload.StateRoot), - ReceiptsRoot: [32]byte(testExecutableData.ExecutionPayload.ReceiptsRoot), - LogsBloom: [256]byte{}, - PrevRandao: [32]byte(testExecutableData.ExecutionPayload.Random), - BlockNumber: testExecutableData.ExecutionPayload.Number, - GasLimit: testExecutableData.ExecutionPayload.GasLimit, - GasUsed: testExecutableData.ExecutionPayload.GasUsed, - Timestamp: testExecutableData.ExecutionPayload.Timestamp, - ExtraData: hexutil.MustDecode("0x0042fafc"), - BaseFeePerGas: uint256.NewInt(16), - BlockHash: expectedMessage.BlockHash, - Transactions: []bellatrix.Transaction{}, - Withdrawals: []*capella.Withdrawal{}, - } - - require.Equal(t, expectedExecutionPayload, blockResponse.Deneb.ExecutionPayload) + require.NotNil(t, blockResponse) + require.Equal(t, expectedMessage, blockResponse.Message) - expectedSignature, err := utils.HexToSignature("0xa2416ce0f5d65329c750ea9338f9b11280aa9b04180859a8a9e8082d1794b9fa17f7928bfab66ed20733d06e6f9ac2ec185ff98b91e5da9aaa6fbab8a410c9f9b76e5fde01d498ee2cfe9867949d2eead6a53e953ad910805f8dddae3ec3bbdf") + require.Equal(t, testExecutableData.ExecutionPayload, blockResponse.ExecutionPayload) + expectedSignature, err := hexutil.Decode("0x72ce97e705ecbe69effb11ae63791fd45f058e5766be52ad6704516d5a984ebf00a22d8661201e21968fbf285700d6ab93accfe54acf2dfc4c52edc190a54fdb00") require.NoError(t, err) - require.Equal(t, expectedSignature, blockResponse.Deneb.Signature) + require.Equal(t, expectedSignature, blockResponse.Signature) } diff --git a/builder/config.go b/builder/config.go index f7c6ff1430..aef5ce663b 100644 --- a/builder/config.go +++ b/builder/config.go @@ -10,19 +10,20 @@ const ( type Config struct { Enabled bool `toml:",omitempty"` IgnoreLatePayloadAttributes bool `toml:",omitempty"` - BuilderSecretKey string `toml:",omitempty"` + BuilderSigningKey string `toml:",omitempty"` ListenAddr string `toml:",omitempty"` GenesisForkVersion string `toml:",omitempty"` BeaconEndpoints []string `toml:",omitempty"` RetryInterval string `toml:",omitempty"` BlockTime time.Duration `toml:",omitempty"` + ProposerAddress string `toml:",omitempty"` } // DefaultConfig is the default config for the builder. var DefaultConfig = Config{ Enabled: false, IgnoreLatePayloadAttributes: false, - BuilderSecretKey: "0x2fc12ae741f29701f8e30f5de6350766c020cb80768a0ff01e6838ffd2431e11", + BuilderSigningKey: "0x2fc12ae741f29701f8e30f5de6350766c020cb80768a0ff01e6838ffd2431e11", ListenAddr: ":28545", GenesisForkVersion: "0x00000000", BeaconEndpoints: []string{"http://127.0.0.1:5052"}, diff --git a/builder/eth_service.go b/builder/eth_service.go index 6bd229df50..4c6b440566 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ethereum/go-ethereum/beacon/engine" + builderTypes "github.com/ethereum/go-ethereum/builder/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -14,7 +15,7 @@ import ( ) type IEthereumService interface { - BuildBlock(attrs *BuilderPayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) + BuildBlock(attrs *builderTypes.PayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) GetBlockByHash(hash common.Hash) *types.Block Config() *params.ChainConfig Synced() bool @@ -32,7 +33,7 @@ func NewEthereumService(eth *eth.Ethereum, config *Config) *EthereumService { } } -func (s *EthereumService) BuildBlock(attrs *BuilderPayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) { +func (s *EthereumService) BuildBlock(attrs *builderTypes.PayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) { // Send a request to generate a full block in the background. // The result can be obtained via the returned channel. args := &miner.BuildPayloadArgs{ diff --git a/builder/eth_service_test.go b/builder/eth_service_test.go index 97bbc0fb56..67bf8dcace 100644 --- a/builder/eth_service_test.go +++ b/builder/eth_service_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + builderTypes "github.com/ethereum/go-ethereum/builder/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -82,7 +83,7 @@ func TestBuildBlock(t *testing.T) { parent := ethservice.BlockChain().CurrentBlock() - testPayloadAttributes := &BuilderPayloadAttributes{ + testPayloadAttributes := &builderTypes.PayloadAttributes{ Timestamp: hexutil.Uint64(parent.Time + 1), Random: common.Hash{0x05, 0x10}, SuggestedFeeRecipient: common.Address{0x04, 0x10}, diff --git a/builder/service.go b/builder/service.go index 201417a872..0b0e4e93eb 100644 --- a/builder/service.go +++ b/builder/service.go @@ -1,25 +1,23 @@ package builder import ( - "errors" + "crypto/ecdsa" "fmt" "net/http" "time" - builderSpec "github.com/attestantio/go-builder-client/spec" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ethereum/go-ethereum/common/hexutil" + builderTypes "github.com/ethereum/go-ethereum/builder/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - "github.com/flashbots/go-boost-utils/bls" - "github.com/flashbots/go-boost-utils/ssz" "github.com/gorilla/mux" ) const ( - _PathGetPayload = "/eth/v1/builder/payload/{slot:[0-9]+}/{parent_hash:0x[a-fA-F0-9]+}" + _PathGetPayload = "/eth/v1/builder/payload" ) type Service struct { @@ -46,7 +44,7 @@ func (s *Service) Stop() error { return nil } -func (s *Service) GetPayloadV1(request PayloadRequestV1) (*builderSpec.VersionedSubmitBlockRequest, error) { +func (s *Service) GetPayloadV1(request *builderTypes.BuilderPayloadRequest) (*builderTypes.VersionedBuilderPayloadResponse, error) { return s.builder.GetPayload(request) } @@ -54,7 +52,7 @@ func NewService(listenAddr string, builder IBuilder) *Service { var srv *http.Server router := mux.NewRouter() - router.HandleFunc(_PathGetPayload, builder.handleGetPayload).Methods(http.MethodGet) + router.HandleFunc(_PathGetPayload, builder.handleGetPayload).Methods(http.MethodPost) srv = &http.Server{ Addr: listenAddr, @@ -68,20 +66,6 @@ func NewService(listenAddr string, builder IBuilder) *Service { } func Register(stack *node.Node, backend *eth.Ethereum, cfg *Config) error { - envBuilderSkBytes, err := hexutil.Decode(cfg.BuilderSecretKey) - if err != nil { - return errors.New("incorrect builder API secret key provided") - } - - genesisForkVersionBytes, err := hexutil.Decode(cfg.GenesisForkVersion) - if err != nil { - return fmt.Errorf("invalid genesisForkVersion: %w", err) - } - - var genesisForkVersion [4]byte - copy(genesisForkVersion[:], genesisForkVersionBytes[:4]) - builderSigningDomain := ssz.ComputeDomain(ssz.DomainTypeAppBuilder, genesisForkVersion, phase0.Root{}) - var beaconClient IBeaconClient if len(cfg.BeaconEndpoints) == 0 { beaconClient = &NilBeaconClient{} @@ -91,11 +75,6 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *Config) error { ethereumService := NewEthereumService(backend, cfg) - builderSk, err := bls.SecretKeyFromBytes(envBuilderSkBytes[:]) - if err != nil { - return errors.New("incorrect builder API secret key provided") - } - var builderRetryInterval time.Duration if cfg.RetryInterval != "" { d, err := time.ParseDuration(cfg.RetryInterval) @@ -107,10 +86,29 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *Config) error { builderRetryInterval = RetryIntervalDefault } + builderPrivateKey, err := crypto.HexToECDSA(cfg.BuilderSigningKey) + if err != nil { + return fmt.Errorf("invalid builder private key: %w", err) + } + builderPublicKey := builderPrivateKey.Public() + builderPublicKeyECDSA, ok := builderPublicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("publicKey could not be converted to ECDSA") + } + builderAddress := crypto.PubkeyToAddress(*builderPublicKeyECDSA) + + var proposerAddress common.Address + if common.IsHexAddress(cfg.ProposerAddress) { + proposerAddress = common.HexToAddress(cfg.ProposerAddress) + } else { + log.Warn("proposer signing address is invalid or not set, proposer signature verification will be skipped") + } + builderArgs := BuilderArgs{ - sk: builderSk, + builderPrivateKey: builderPrivateKey, + builderAddress: builderAddress, + proposerAddress: proposerAddress, eth: ethereumService, - builderSigningDomain: builderSigningDomain, builderRetryInterval: builderRetryInterval, ignoreLatePayloadAttributes: cfg.IgnoreLatePayloadAttributes, beaconClient: beaconClient, diff --git a/builder/types.go b/builder/types/attributes.go similarity index 85% rename from builder/types.go rename to builder/types/attributes.go index 06accfe6f1..54c12c2223 100644 --- a/builder/types.go +++ b/builder/types/attributes.go @@ -1,4 +1,4 @@ -package builder +package types import ( "github.com/ethereum/go-ethereum/common" @@ -7,12 +7,7 @@ import ( "golang.org/x/exp/slices" ) -type PayloadRequestV1 struct { - Slot uint64 `json:"slot"` - ParentHash common.Hash `json:"parentHash"` -} - -type BuilderPayloadAttributes struct { +type PayloadAttributes struct { Timestamp hexutil.Uint64 `json:"timestamp"` Random common.Hash `json:"prevRandao"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient,omitempty"` @@ -26,7 +21,7 @@ type BuilderPayloadAttributes struct { Transactions []*types.Transaction `json:"transactions"` // Optimism addition: txs forced into the block via engine API } -func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bool { +func (attrs *PayloadAttributes) Equal(other *PayloadAttributes) bool { if attrs.Timestamp != other.Timestamp || attrs.Random != other.Random || attrs.SuggestedFeeRecipient != other.SuggestedFeeRecipient || diff --git a/builder/types/payload.go b/builder/types/payload.go new file mode 100644 index 0000000000..e637a63dac --- /dev/null +++ b/builder/types/payload.go @@ -0,0 +1,254 @@ +package types + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" +) + +var SigningDomainBuilderV1 = [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + +type PayloadRequestV1 struct { + Slot uint64 `json:"slot"` + ParentHash common.Hash `json:"parentHash"` +} + +// MarshalJSON marshals as JSON. +func (p *PayloadRequestV1) MarshalJSON() ([]byte, error) { + type PayloadRequestV1 struct { + Slot hexutil.Uint64 `json:"slot"` + ParentHash common.Hash `json:"parentHash"` + } + + var enc PayloadRequestV1 + enc.Slot = hexutil.Uint64(p.Slot) + enc.ParentHash = p.ParentHash + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (p *PayloadRequestV1) UnmarshalJSON(input []byte) error { + type PayloadRequestV1 struct { + Slot *hexutil.Uint64 `json:"slot"` + ParentHash *common.Hash `json:"parentHash"` + } + + var dec PayloadRequestV1 + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + + if dec.Slot == nil { + return errors.New("missing required field 'slot' for PayloadRequestV1") + } + p.Slot = uint64(*dec.Slot) + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for PayloadRequestV1") + } + p.ParentHash = *dec.ParentHash + return nil +} + +type BuilderPayloadRequest struct { + Message PayloadRequestV1 `json:"message"` + Signature []byte `json:"signature"` +} + +// MarshalJSON marshals as JSON. +func (p *BuilderPayloadRequest) MarshalJSON() ([]byte, error) { + type BuilderPayloadRequest struct { + Message PayloadRequestV1 `json:"message"` + Signature hexutil.Bytes `json:"signature"` + } + + var enc BuilderPayloadRequest + enc.Message = p.Message + enc.Signature = p.Signature + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (p *BuilderPayloadRequest) UnmarshalJSON(input []byte) error { + type BuilderPayloadRequest struct { + Message *PayloadRequestV1 `json:"message"` + Signature *hexutil.Bytes `json:"signature"` + } + + var dec BuilderPayloadRequest + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + + if dec.Message == nil { + return errors.New("missing required field 'message' for PayloadRequestV1") + } + p.Message = *dec.Message + if dec.Signature == nil { + return errors.New("missing required field 'signature' for PayloadRequestV1") + } + p.Signature = *dec.Signature + return nil +} + +// BidTrace represents a bid trace. +type BidTrace struct { + Slot uint64 `json:"slot"` + ParentHash common.Hash `json:"parentHash"` + BlockHash common.Hash `json:"blockHash"` + BuilderAddress common.Address `json:"builderAddress"` + ProposerAddress common.Address `json:"proposerAddress"` + ProposerFeeRecipient common.Address `json:"proposerFeeRecipient"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Value *big.Int `json:"value"` +} + +// MarshalJSON marshals as JSON. +func (b *BidTrace) MarshalJSON() ([]byte, error) { + type BidTrace struct { + Slot hexutil.Uint64 `json:"slot"` + ParentHash common.Hash `json:"parentHash"` + BlockHash common.Hash `json:"blockHash"` + BuilderAddress common.Address `json:"builderAddress"` + ProposerAddress common.Address `json:"proposerAddress"` + ProposerFeeRecipient common.Address `json:"proposerFeeRecipient"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Value *hexutil.Big `json:"value"` + } + + var enc BidTrace + enc.Slot = hexutil.Uint64(b.Slot) + enc.ParentHash = b.ParentHash + enc.BlockHash = b.BlockHash + enc.BuilderAddress = b.BuilderAddress + enc.ProposerAddress = b.ProposerAddress + enc.ProposerFeeRecipient = b.ProposerFeeRecipient + enc.GasLimit = hexutil.Uint64(b.GasLimit) + enc.GasUsed = hexutil.Uint64(b.GasUsed) + enc.Value = (*hexutil.Big)(b.Value) + return json.Marshal(enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (b *BidTrace) UnmarshalJSON(input []byte) error { + type BidTrace struct { + Slot *hexutil.Uint64 `json:"slot"` + ParentHash *common.Hash `json:"parentHash"` + BlockHash *common.Hash `json:"blockHash"` + BuilderAddress *common.Address `json:"builderAddress"` + ProposerAddress *common.Address `json:"proposerAddress"` + ProposerFeeRecipient *common.Address `json:"proposerFeeRecipient"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Value *hexutil.Big `json:"value"` + } + + var dec BidTrace + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + + if dec.Slot == nil { + return errors.New("missing required field 'slot' for BidTrace") + } + b.Slot = uint64(*dec.Slot) + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for BidTrace") + } + b.ParentHash = *dec.ParentHash + if dec.BlockHash == nil { + return errors.New("missing required field 'blockHash' for BidTrace") + } + b.BlockHash = *dec.BlockHash + if dec.BuilderAddress == nil { + return errors.New("missing required field 'builderAddress' for BidTrace") + } + b.BuilderAddress = *dec.BuilderAddress + if dec.ProposerAddress == nil { + return errors.New("missing required field 'proposerAddress' for BidTrace") + } + b.ProposerAddress = *dec.ProposerAddress + if dec.ProposerFeeRecipient == nil { + return errors.New("missing required field 'proposerFeeRecipient' for BidTrace") + } + b.ProposerFeeRecipient = *dec.ProposerFeeRecipient + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for BidTrace") + } + b.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for BidTrace") + } + b.GasUsed = uint64(*dec.GasUsed) + if dec.Value == nil { + return errors.New("missing required field 'value' for BidTrace") + } + b.Value = (*big.Int)(dec.Value) + if b.Value == nil { + return errors.New("missing required field 'value' for BidTrace") + } + return nil +} + +// VersionedBuilderPayloadResponse contains a versioned signed builder payload. +type VersionedBuilderPayloadResponse struct { + Version SpecVersion `json:"version"` + Message *BidTrace `json:"message"` + ExecutionPayload *engine.ExecutableData `json:"executionPayload"` + Signature []byte `json:"signature"` +} + +// MarshalJSON marshals as JSON. +func (p *VersionedBuilderPayloadResponse) MarshalJSON() ([]byte, error) { + type VersionedBuilderPayloadResponse struct { + Version *SpecVersion `json:"version"` + Message *BidTrace `json:"message"` + ExecutionPayload *engine.ExecutableData `json:"executionPayload"` + Signature hexutil.Bytes `json:"signature"` + } + + var enc VersionedBuilderPayloadResponse + enc.Version = &p.Version + enc.Message = p.Message + enc.ExecutionPayload = p.ExecutionPayload + enc.Signature = p.Signature + return json.Marshal(enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (p *VersionedBuilderPayloadResponse) UnmarshalJSON(input []byte) error { + type VersionedBuilderPayloadResponse struct { + Version *SpecVersion `json:"version"` + Message *BidTrace `json:"message"` + ExecutionPayload *engine.ExecutableData `json:"executionPayload"` + Signature hexutil.Bytes `json:"signature"` + } + + var dec VersionedBuilderPayloadResponse + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + + if dec.Version == nil { + return errors.New("missing required field 'version' for VersionedBuilderPayloadResponse") + } + p.Version = *dec.Version + if dec.Message == nil { + return errors.New("missing required field 'message' for VersionedBuilderPayloadResponse") + } + p.Message = dec.Message + if dec.ExecutionPayload == nil { + return errors.New("missing required field 'executionPayload' for VersionedBuilderPayloadResponse") + } + p.ExecutionPayload = dec.ExecutionPayload + if dec.Signature == nil { + return errors.New("missing required field 'signature' for VersionedBuilderPayloadResponse") + } + p.Signature = dec.Signature + return nil +} diff --git a/builder/types/payload_test.go b/builder/types/payload_test.go new file mode 100644 index 0000000000..72439a5005 --- /dev/null +++ b/builder/types/payload_test.go @@ -0,0 +1,133 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" +) + +func TestBuilderPayloadRequest_MarshalJSON(t *testing.T) { + signature := [65]byte{0x1, 0x2} + payload := BuilderPayloadRequest{ + Message: PayloadRequestV1{ + Slot: 1, + ParentHash: common.Hash{0x2, 03}, + }, + Signature: signature[:], + } + json, err := payload.MarshalJSON() + require.NoError(t, err) + payloadStr := `{"message":{"slot":"0x1","parentHash":"0x0203000000000000000000000000000000000000000000000000000000000000"},"signature":"0x0102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}` + require.Equal(t, payloadStr, string(json)) +} + +func TestBuilderPayloadRequest_UnmarshalJSON(t *testing.T) { + payloadStr := `{"message":{"slot":"0xa","parentHash":"0x0405000000000000000000000000000000000000000000000000000000000000"},"signature":"0x0203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}` + payload := BuilderPayloadRequest{} + signature := [65]byte{0x2, 0x3} + err := payload.UnmarshalJSON([]byte(payloadStr)) + require.NoError(t, err) + require.Equal(t, payload.Message.Slot, uint64(10)) + require.Equal(t, payload.Message.ParentHash, common.Hash{0x4, 05}) + require.Equal(t, payload.Signature, signature[:]) +} + +func TestVersionedBuilderPayloadResponse_MarshalJSON(t *testing.T) { + bidTrace := BidTrace{ + Slot: uint64(1), + ParentHash: common.Hash{0x1}, + BlockHash: common.Hash{0x2}, + BuilderAddress: common.Address{0x3}, + ProposerAddress: common.Address{0x4}, + ProposerFeeRecipient: common.Address{0x5}, + GasLimit: uint64(100), + GasUsed: uint64(90), + Value: big.NewInt(30), + } + zero := uint64(0) + executionPayload := &engine.ExecutableData{ + ParentHash: common.Hash{0x02, 0x03}, + FeeRecipient: common.Address{0x04, 0x05}, + StateRoot: common.Hash{0x07, 0x16}, + ReceiptsRoot: common.Hash{0x08, 0x20}, + Random: common.Hash{0x09, 0x30}, + LogsBloom: types.Bloom{}.Bytes(), + Number: uint64(10), + GasLimit: uint64(100), + GasUsed: uint64(100), + Timestamp: uint64(105), + ExtraData: hexutil.MustDecode("0x0042fafc"), + + BaseFeePerGas: big.NewInt(16), + + BlockHash: common.Hash{0x09, 0x30}, + Transactions: [][]byte{}, + Withdrawals: types.Withdrawals{}, + BlobGasUsed: &zero, + ExcessBlobGas: &zero, + } + versionedBuilderPayloadResponse := VersionedBuilderPayloadResponse{ + Version: SpecVersionEcotone, + Message: &bidTrace, + ExecutionPayload: executionPayload, + Signature: []byte{0x1, 0x2}, + } + + json, err := versionedBuilderPayloadResponse.MarshalJSON() + require.NoError(t, err) + expectedStr := `{"version":"ecotone","message":{"slot":"0x1","parentHash":"0x0100000000000000000000000000000000000000000000000000000000000000","blockHash":"0x0200000000000000000000000000000000000000000000000000000000000000","builderAddress":"0x0300000000000000000000000000000000000000","proposerAddress":"0x0400000000000000000000000000000000000000","proposerFeeRecipient":"0x0500000000000000000000000000000000000000","gasLimit":"0x64","gasUsed":"0x5a","value":"0x1e"},"executionPayload":{"parentHash":"0x0203000000000000000000000000000000000000000000000000000000000000","feeRecipient":"0x0405000000000000000000000000000000000000","stateRoot":"0x0716000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0820000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0x0930000000000000000000000000000000000000000000000000000000000000","blockNumber":"0xa","gasLimit":"0x64","gasUsed":"0x64","timestamp":"0x69","extraData":"0x0042fafc","baseFeePerGas":"0x10","blockHash":"0x0930000000000000000000000000000000000000000000000000000000000000","transactions":[],"withdrawals":[],"blobGasUsed":"0x0","excessBlobGas":"0x0"},"signature":"0x0102"}` + require.Equal(t, expectedStr, string(json)) +} + +func TestVersionedBuilderPayloadResponse_UnmarshalJSON(t *testing.T) { + jsonStr := `{"version":"canyon","message":{"slot":"0xa","parentHash":"0x0200000000000000000000000000000000000000000000000000000000000000","blockHash":"0x0300000000000000000000000000000000000000000000000000000000000000","builderAddress":"0x0300000000000000000000000000000000000000","proposerAddress":"0x0400000000000000000000000000000000000000","proposerFeeRecipient":"0x0500000000000000000000000000000000000000","gasLimit":"0x64","gasUsed":"0x5a","value":"0x1e"},"executionPayload":{"parentHash":"0x0203000000000000000000000000000000000000000000000000000000000000","feeRecipient":"0x0405000000000000000000000000000000000000","stateRoot":"0x0716000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0820000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0x0930000000000000000000000000000000000000000000000000000000000000","blockNumber":"0xa","gasLimit":"0x64","gasUsed":"0x64","timestamp":"0x69","extraData":"0x0042fafc","baseFeePerGas":"0x10","blockHash":"0x0930000000000000000000000000000000000000000000000000000000000000","transactions":[],"withdrawals":[],"blobGasUsed":"0x0","excessBlobGas":"0x0"},"signature":"0x0102"}` + versionedBuilderPayloadResponse := VersionedBuilderPayloadResponse{} + err := versionedBuilderPayloadResponse.UnmarshalJSON([]byte(jsonStr)) + require.NoError(t, err) + + bidTrace := BidTrace{ + Slot: uint64(10), + ParentHash: common.Hash{0x2}, + BlockHash: common.Hash{0x3}, + BuilderAddress: common.Address{0x3}, + ProposerAddress: common.Address{0x4}, + ProposerFeeRecipient: common.Address{0x5}, + GasLimit: uint64(100), + GasUsed: uint64(90), + Value: big.NewInt(30), + } + zero := uint64(0) + executionPayload := &engine.ExecutableData{ + ParentHash: common.Hash{0x02, 0x03}, + FeeRecipient: common.Address{0x04, 0x05}, + StateRoot: common.Hash{0x07, 0x16}, + ReceiptsRoot: common.Hash{0x08, 0x20}, + Random: common.Hash{0x09, 0x30}, + LogsBloom: types.Bloom{}.Bytes(), + Number: uint64(10), + GasLimit: uint64(100), + GasUsed: uint64(100), + Timestamp: uint64(105), + ExtraData: hexutil.MustDecode("0x0042fafc"), + + BaseFeePerGas: big.NewInt(16), + + BlockHash: common.Hash{0x09, 0x30}, + Transactions: [][]byte{}, + Withdrawals: types.Withdrawals{}, + BlobGasUsed: &zero, + ExcessBlobGas: &zero, + } + expectedVersionedBuilderPayloadResponse := VersionedBuilderPayloadResponse{ + Version: SpecVersionCanyon, + Message: &bidTrace, + ExecutionPayload: executionPayload, + Signature: []byte{0x1, 0x2}, + } + require.Equal(t, expectedVersionedBuilderPayloadResponse, versionedBuilderPayloadResponse) +} diff --git a/builder/types/version.go b/builder/types/version.go new file mode 100644 index 0000000000..025b60606c --- /dev/null +++ b/builder/types/version.go @@ -0,0 +1,58 @@ +package types + +import ( + "fmt" + "strings" +) + +// SpecVersion defines the version of the spec in the payload. +type SpecVersion uint64 + +const ( + // Unknown is an unknown spec version. + Unknown SpecVersion = iota + // SpecVersionBedrock is the spec version equivalent to the bellatrix release of the l1 beacon chain. + SpecVersionBedrock + // SpecVersionCanyon is the spec version equivalent to the capella release of the l1 beacon chain. + SpecVersionCanyon + // SpecVersionEcotone is the spec version equivalent to the deneb release of the l1 beacon chain. + SpecVersionEcotone +) + +var specVersionStrings = [...]string{ + "unknown", + "bedrock", + "canyon", + "ecotone", +} + +// MarshalJSON implements json.Marshaler. +func (d *SpecVersion) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", specVersionStrings[*d])), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (d *SpecVersion) UnmarshalJSON(input []byte) error { + var err error + switch strings.ToLower(string(input)) { + case `"bedrock"`: + *d = SpecVersionBedrock + case `"canyon"`: + *d = SpecVersionCanyon + case `"ecotone"`: + *d = SpecVersionEcotone + default: + err = fmt.Errorf("unrecognised spec version %s", string(input)) + } + + return err +} + +// String returns a string representation of the struct. +func (d SpecVersion) String() string { + if int(d) >= len(specVersionStrings) { + return "unknown" + } + + return specVersionStrings[d] +} diff --git a/builder/utils.go b/builder/utils.go index d9a1f35dc3..c380df9fcb 100644 --- a/builder/utils.go +++ b/builder/utils.go @@ -4,8 +4,14 @@ import ( "context" "encoding/json" "errors" + "math/big" "net/http" "time" + + builderTypes "github.com/ethereum/go-ethereum/builder/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" ) type httpErrorResp struct { @@ -64,3 +70,22 @@ func runRetryLoop(ctx context.Context, interval time.Duration, retry func()) { } } } + +func SigningHash(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) { + var msgInput [32 + 32 + 32]byte + // domain: first 32 bytes + copy(msgInput[:32], domain[:]) + // chain_id: second 32 bytes + if chainID.BitLen() > 256 { + return common.Hash{}, errors.New("chain_id is too large") + } + chainID.FillBytes(msgInput[32:64]) + // payload_hash: third 32 bytes, hash of encoded payload + copy(msgInput[64:], crypto.Keccak256(payloadBytes)) + + return crypto.Keccak256Hash(msgInput[:]), nil +} + +func BuilderSigningHash(cfg *params.ChainConfig, payloadBytes []byte) (common.Hash, error) { + return SigningHash(builderTypes.SigningDomainBuilderV1, cfg.ChainID, payloadBytes) +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 132e00be7f..a634a56ea9 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -179,12 +179,12 @@ var ( builderApiFlags = []cli.Flag{ utils.BuilderEnabled, utils.BuilderIgnoreLatePayloadAttributes, - utils.BuilderSecretKey, + utils.BuilderSigningKey, utils.BuilderListenAddr, - utils.BuilderGenesisForkVersion, utils.BuilderBeaconEndpoints, utils.BuilderBlockRetryInterval, utils.BuilderBlockTime, + utils.BuilderProposerSigningAddress, } rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 70e1969bfa..3ed6b0fd38 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -683,10 +683,10 @@ var ( EnvVars: []string{"BUILDER_IGNORE_LATE_PAYLOAD_ATTRIBUTES"}, Category: flags.BuilderCategory, } - BuilderSecretKey = &cli.StringFlag{ - Name: "builder.secret_key", + BuilderSigningKey = &cli.StringFlag{ + Name: "builder.signing_key", Usage: "Builder key used for signing blocks", - EnvVars: []string{"BUILDER_SECRET_KEY"}, + EnvVars: []string{"BUILDER_SIGNING_KEY"}, Value: "0x2fc12ae741f29701f8e30f5de6350766c020cb80768a0ff01e6838ffd2431e11", Category: flags.BuilderCategory, } @@ -697,13 +697,6 @@ var ( Value: ":28545", Category: flags.BuilderCategory, } - BuilderGenesisForkVersion = &cli.StringFlag{ - Name: "builder.genesis_fork_version", - Usage: "Gensis fork version.", - EnvVars: []string{"BUILDER_GENESIS_FORK_VERSION"}, - Value: "0x00000000", - Category: flags.BuilderCategory, - } BuilderBeaconEndpoints = &cli.StringFlag{ Name: "builder.beacon_endpoints", Usage: "Comma separated list of beacon endpoints to connect to for beacon chain data", @@ -725,6 +718,13 @@ var ( Value: builder.RetryIntervalDefault.String(), Category: flags.BuilderCategory, } + BuilderProposerSigningAddress = &cli.StringFlag{ + Name: "builder.proposer_signing_address", + Usage: "Proposer address used for authenticating proposer messages", + EnvVars: []string{"BUILDER_PROPOSER_SIGNING_ADDRESS"}, + Category: flags.BuilderCategory, + } + CustomChainFlag = &cli.StringFlag{ Name: "chain", Usage: "Path to a custom chain specification file", @@ -1582,13 +1582,13 @@ func SetBuilderConfig(ctx *cli.Context, cfg *builder.Config) { cfg.Enabled = ctx.Bool(BuilderEnabled.Name) } cfg.IgnoreLatePayloadAttributes = ctx.IsSet(BuilderIgnoreLatePayloadAttributes.Name) - cfg.BuilderSecretKey = ctx.String(BuilderSecretKey.Name) + cfg.BuilderSigningKey = ctx.String(BuilderSigningKey.Name) cfg.ListenAddr = ctx.String(BuilderListenAddr.Name) - cfg.GenesisForkVersion = ctx.String(BuilderGenesisForkVersion.Name) cfg.BeaconEndpoints = strings.Split(ctx.String(BuilderBeaconEndpoints.Name), ",") cfg.RetryInterval = ctx.String(BuilderBlockRetryInterval.Name) cfg.BlockTime = ctx.Duration(BuilderBlockTime.Name) + cfg.ProposerAddress = ctx.String(BuilderProposerSigningAddress.Name) } // SetNodeConfig applies node-related command line flags to the config. diff --git a/go.mod b/go.mod index b40052057f..a02ff2e67a 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 github.com/Microsoft/go-winio v0.6.2 github.com/VictoriaMetrics/fastcache v1.12.2 - github.com/attestantio/go-builder-client v0.5.0 - github.com/attestantio/go-eth2-client v0.21.10 github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/config v1.18.45 github.com/aws/aws-sdk-go-v2/credentials v1.13.43 @@ -31,7 +29,6 @@ require ( github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.3 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e - github.com/flashbots/go-boost-utils v1.8.1 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gofrs/flock v0.8.1 @@ -59,6 +56,7 @@ require ( github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 + github.com/pkg/errors v0.9.1 github.com/protolambda/bls12-381-util v0.1.0 github.com/protolambda/zrnt v0.32.2 github.com/protolambda/ztyp v0.2.2 @@ -118,7 +116,6 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/goccy/go-yaml v1.11.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -140,13 +137,11 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/opentracing/opentracing-go v1.1.0 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 25abf88be8..052e4dcc4a 100644 --- a/go.sum +++ b/go.sum @@ -22,10 +22,6 @@ github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjC github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/attestantio/go-builder-client v0.5.0 h1:DxYunaN2U7Q8wRf83FzWwanZ2brds4BJclFLxAk/W6s= -github.com/attestantio/go-builder-client v0.5.0/go.mod h1:1/ewo8zF6++C6Fldvtq5hjhp9ZAafIK91Vp7XrmUZsE= -github.com/attestantio/go-eth2-client v0.21.10 h1:1DWn42WKjk8mR8jKkjbaDCGNMVnh2IfAWRUmt7iemRo= -github.com/attestantio/go-eth2-client v0.21.10/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/config v1.18.45 h1:Aka9bI7n8ysuwPeFdm77nfbyHCAKQ3z9ghB3S/38zes= @@ -133,8 +129,6 @@ github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16M github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/flashbots/go-boost-utils v1.8.1 h1:AD+1+4oCbBjXLK8IqWHYznD95K6/MmqXhozv5fFOCkU= -github.com/flashbots/go-boost-utils v1.8.1/go.mod h1:jFi2H1el7jGPr2ShkWpYPfKsY9vwsFNmBPJRCO7IPg8= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -155,18 +149,10 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ= -github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -226,10 +212,6 @@ github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25 github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/go-clone v1.6.0 h1:HMo5uvg4wgfiy5FoGOqlFLQED/VGRm2D9Pi8g1FXPGc= -github.com/huandu/go-clone v1.6.0/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE= -github.com/huandu/go-clone/generic v1.6.0 h1:Wgmt/fUZ28r16F2Y3APotFD59sHk1p78K0XLdbUYN5U= -github.com/huandu/go-clone/generic v1.6.0/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= @@ -272,8 +254,6 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -350,8 +330,6 @@ github.com/protolambda/zrnt v0.32.2 h1:KZ48T+3UhsPXNdtE/5QEvGc9DGjUaRI17nJaoznoI github.com/protolambda/zrnt v0.32.2/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY= github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU= -github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe+abbzfx9kCPeXIW4fiWyDdxlwHw07j8UGhdTd4= -github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2O7ZhWYVGYSR2iVcjzQuPV+o= github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -386,8 +364,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c h1:4WU+p200eLYtBsx3M5CKXvkjVdf5SC3W9nMg37y0TFI= -github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c/go.mod h1:f3jBhpWvuZmue0HZK52GzRHJOYHYSILs/c8+K2S/J+o= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg=