diff --git a/builder/builder.go b/builder/builder.go index dc284b49d4f9..7051da418c55 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -1,7 +1,6 @@ package builder import ( - "encoding/json" _ "os" "github.com/ethereum/go-ethereum/common/hexutil" @@ -33,28 +32,29 @@ type IRelay interface { GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) } +type IBuilder interface { + OnPayloadAttribute(attrs *BuilderPayloadAttributes) error +} + type Builder struct { beaconClient IBeaconClient relay IRelay + eth IEthereumService builderSecretKey *bls.SecretKey builderPublicKey boostTypes.PublicKey builderSigningDomain boostTypes.Domain } -func NewBuilder(sk *bls.SecretKey, bc IBeaconClient, relay IRelay, builderSigningDomain boostTypes.Domain) *Builder { +func NewBuilder(sk *bls.SecretKey, bc IBeaconClient, relay IRelay, builderSigningDomain boostTypes.Domain, eth IEthereumService) *Builder { pkBytes := bls.PublicKeyFromSecretKey(sk).Compress() pk := boostTypes.PublicKey{} pk.FromSlice(pkBytes) - _, err := bc.onForkchoiceUpdate() - if err != nil { - log.Error("could not initialize beacon client", "err", err) - } - return &Builder{ beaconClient: bc, relay: relay, + eth: eth, builderSecretKey: sk, builderPublicKey: pk, @@ -62,91 +62,76 @@ func NewBuilder(sk *bls.SecretKey, bc IBeaconClient, relay IRelay, builderSignin } } -func (b *Builder) onForkchoice(payloadAttributes *beacon.PayloadAttributesV1) { - dataJson, err := json.Marshal(payloadAttributes) - if err == nil { - log.Info("FCU", "data", string(dataJson)) - } else { - log.Info("FCU", "data", payloadAttributes, "parsingError", err) - - } - - nextSlot, err := b.beaconClient.onForkchoiceUpdate() - if err != nil { - log.Error("FCU hook failed", "err", err) - return - } - - if payloadAttributes != nil { - payloadAttributes.Slot = nextSlot - if vd, err := b.relay.GetValidatorForSlot(nextSlot); err == nil { - payloadAttributes.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) - payloadAttributes.GasLimit = vd.GasLimit +func (b *Builder) OnPayloadAttribute(attrs *BuilderPayloadAttributes) error { + if attrs != nil { + vd, err := b.relay.GetValidatorForSlot(attrs.Slot) + if err != nil { + log.Info("could not get validator while submitting block", "err", err, "slot", attrs.Slot) + return err } - } -} -func (b *Builder) newSealedBlock(data *beacon.ExecutableDataV1, block *types.Block, payloadAttributes *beacon.PayloadAttributesV1) { - dataJson, err := json.Marshal(data) - if err == nil { - log.Info("newSealedBlock", "data", string(dataJson)) - } else { - log.Info("newSealedBlock", "data", data, "parsingError", err) - } - payload, err := executableDataToExecutionPayload(data) - if err != nil { - log.Error("could not format execution payload", "err", err) - return - } - - vd, err := b.relay.GetValidatorForSlot(payloadAttributes.Slot) - if err != nil { - log.Error("could not get validator while submitting block", "err", err, "slot", payloadAttributes.Slot) - return - } - - pubkey, err := boostTypes.HexToPubkey(string(vd.Pubkey)) - if err != nil { - log.Error("could not parse pubkey", "err", err, "pubkey", vd.Pubkey) - return - } - - value := new(boostTypes.U256Str) - err = value.FromBig(block.Profit) - if err != nil { - log.Error("could not set block value", "err", err) - return - } - - blockBidMsg := boostTypes.BidTrace{ - Slot: payloadAttributes.Slot, - ParentHash: payload.ParentHash, - BlockHash: payload.BlockHash, - BuilderPubkey: b.builderPublicKey, - ProposerPubkey: pubkey, - ProposerFeeRecipient: boostTypes.Address(payloadAttributes.SuggestedFeeRecipient), - GasLimit: data.GasLimit, - GasUsed: data.GasUsed, - Value: *value, - } - - signature, err := boostTypes.SignMessage(&blockBidMsg, b.builderSigningDomain, b.builderSecretKey) - if err != nil { - log.Error("could not sign builder bid", "err", err) - return - } - - blockSubmitReq := boostTypes.BuilderSubmitBlockRequest{ - Signature: signature, - Message: &blockBidMsg, - ExecutionPayload: payload, - } - - err = b.relay.SubmitBlock(&blockSubmitReq) - if err != nil { - log.Error("could not submit block", "err", err) - return + attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) + attrs.GasLimit = vd.GasLimit + + if b.eth.Synced() { + block := b.eth.GetBlockByHash(attrs.HeadHash) + if block == nil { + log.Info("Block hash not found in blocktree", "head block hash", attrs.HeadHash) + return err + } + + executableData := b.eth.BuildBlock(attrs) + payload, err := executableDataToExecutionPayload(executableData) + if err != nil { + log.Error("could not format execution payload", "err", err) + return err + } + + pubkey, err := boostTypes.HexToPubkey(string(vd.Pubkey)) + if err != nil { + log.Error("could not parse pubkey", "err", err, "pubkey", vd.Pubkey) + return err + } + + value := new(boostTypes.U256Str) + err = value.FromBig(block.Profit) + if err != nil { + log.Error("could not set block value", "err", err) + return err + } + + blockBidMsg := boostTypes.BidTrace{ + Slot: attrs.Slot, + ParentHash: payload.ParentHash, + BlockHash: payload.BlockHash, + BuilderPubkey: b.builderPublicKey, + ProposerPubkey: pubkey, + ProposerFeeRecipient: boostTypes.Address(attrs.SuggestedFeeRecipient), + GasLimit: executableData.GasLimit, + GasUsed: executableData.GasUsed, + Value: *value, + } + + signature, err := boostTypes.SignMessage(&blockBidMsg, b.builderSigningDomain, b.builderSecretKey) + if err != nil { + log.Error("could not sign builder bid", "err", err) + return err + } + + blockSubmitReq := boostTypes.BuilderSubmitBlockRequest{ + Signature: signature, + Message: &blockBidMsg, + ExecutionPayload: payload, + } + + err = b.relay.SubmitBlock(&blockSubmitReq) + if err != nil { + log.Error("could not submit block", "err", err) + return err + } + } } + return nil } func executableDataToExecutionPayload(data *beacon.ExecutableDataV1) (*boostTypes.ExecutionPayload, error) { diff --git a/builder/builder_test.go b/builder/builder_test.go index 869340d13b08..f3df52a1e430 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestOnNewSealedBlock(t *testing.T) { +func TestOnPayloadAttributes(t *testing.T) { vsk, err := bls.SecretKeyFromBytes(hexutil.MustDecode("0x370bb8c1a6e62b2882f6ec76762a67b39609002076b95aae5b023997cf9b2dc9")) require.NoError(t, err) validator := &ValidatorPrivateData{ @@ -41,11 +41,9 @@ func TestOnNewSealedBlock(t *testing.T) { bDomain := boostTypes.ComputeDomain(boostTypes.DomainTypeAppBuilder, [4]byte{0x02, 0x0, 0x0, 0x0}, boostTypes.Hash{}) - builder := NewBuilder(sk, &testBeacon, &testRelay, bDomain) - testExecutableData := &beacon.ExecutableDataV1{ ParentHash: common.Hash{0x02, 0x03}, - FeeRecipient: common.Address{0x06, 0x15}, + FeeRecipient: common.Address(feeRecipient), StateRoot: common.Hash{0x07, 0x16}, ReceiptsRoot: common.Hash{0x08, 0x20}, LogsBloom: hexutil.MustDecode("0x000000000000000000000000000000"), @@ -65,18 +63,21 @@ func TestOnNewSealedBlock(t *testing.T) { Profit: big.NewInt(10), } - testPayloadAttributes := &beacon.PayloadAttributesV1{ - Timestamp: uint64(104), + testPayloadAttributes := &BuilderPayloadAttributes{ + Timestamp: hexutil.Uint64(104), Random: common.Hash{0x05, 0x10}, SuggestedFeeRecipient: common.Address{0x04, 0x10}, GasLimit: uint64(21), Slot: uint64(25), } - builder.newSealedBlock(testExecutableData, testBlock, testPayloadAttributes) + testEthService := &testEthereumService{synced: true, testExecutableData: testExecutableData, testBlock: testBlock} - require.NotNil(t, testRelay.submittedMsg) + builder := NewBuilder(sk, &testBeacon, &testRelay, bDomain, testEthService) + + builder.OnPayloadAttribute(testPayloadAttributes) + require.NotNil(t, testRelay.submittedMsg) expectedProposerPubkey, err := boostTypes.HexToPubkey(testBeacon.validator.Pk.String()) require.NoError(t, err) @@ -86,7 +87,7 @@ func TestOnNewSealedBlock(t *testing.T) { BlockHash: boostTypes.Hash{0x09, 0xff}, BuilderPubkey: builder.builderPublicKey, ProposerPubkey: expectedProposerPubkey, - ProposerFeeRecipient: boostTypes.Address{0x04, 0x10}, + ProposerFeeRecipient: feeRecipient, GasLimit: uint64(50), GasUsed: uint64(100), Value: boostTypes.U256Str{0x0a}, @@ -96,7 +97,7 @@ func TestOnNewSealedBlock(t *testing.T) { expectedExecutionPayload := boostTypes.ExecutionPayload{ ParentHash: [32]byte(testExecutableData.ParentHash), - FeeRecipient: boostTypes.Address{0x6, 0x15}, + FeeRecipient: feeRecipient, StateRoot: [32]byte(testExecutableData.StateRoot), ReceiptsRoot: [32]byte(testExecutableData.ReceiptsRoot), LogsBloom: [256]byte{}, @@ -110,9 +111,10 @@ func TestOnNewSealedBlock(t *testing.T) { BlockHash: boostTypes.Hash{0x09, 0xff}, Transactions: []hexutil.Bytes{}, } + require.Equal(t, expectedExecutionPayload, *testRelay.submittedMsg.ExecutionPayload) - expectedSignature, err := boostTypes.HexToSignature("0xadebce714127deea6b04c8f63e650ad6b4c0d3df14ecd9759bef741cd6d72509090f5e172033ce40475c322c0c0e3fae0e78a880a66cb324913ea490472d93e187a9a91284b05137f1554688c5e9b1ee73539a2b005b103e8bd50e973e8e0f49") + expectedSignature, err := boostTypes.HexToSignature("0xb086abc231a515559128122a6618ad316a76195ad39aa28195c9e8921b98561ca4fd12e2e1ea8d50d8e22f7e36d42ee1084fef26672beceda7650a87061e412d7742705077ac3af3ca1a1c3494eccb22fe7c234fd547a285ba699ff87f0e7759") require.NoError(t, err) require.Equal(t, expectedSignature, testRelay.submittedMsg.Signature) diff --git a/builder/eth_service.go b/builder/eth_service.go new file mode 100644 index 000000000000..4986e0d44235 --- /dev/null +++ b/builder/eth_service.go @@ -0,0 +1,60 @@ +package builder + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/beacon" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" + "github.com/ethereum/go-ethereum/log" +) + +type IEthereumService interface { + BuildBlock(attrs *BuilderPayloadAttributes) *beacon.ExecutableDataV1 + GetBlockByHash(hash common.Hash) *types.Block + Synced() bool +} + +type testEthereumService struct { + synced bool + testExecutableData *beacon.ExecutableDataV1 + testBlock *types.Block +} + +func (t *testEthereumService) BuildBlock(attrs *BuilderPayloadAttributes) *beacon.ExecutableDataV1 { + return t.testExecutableData +} + +func (t *testEthereumService) GetBlockByHash(hash common.Hash) *types.Block { return t.testBlock } + +func (t *testEthereumService) Synced() bool { return t.synced } + +type EthereumService struct { + eth *eth.Ethereum +} + +func NewEthereumService(eth *eth.Ethereum) *EthereumService { + return &EthereumService{eth: eth} +} + +func (s *EthereumService) BuildBlock(attrs *BuilderPayloadAttributes) *beacon.ExecutableDataV1 { + // Send a request to generate a full block in the background. + // The result can be obtained via the returned channel. + resCh, err := s.eth.Miner().GetSealingBlockAsync(attrs.HeadHash, uint64(attrs.Timestamp), attrs.SuggestedFeeRecipient, attrs.GasLimit, attrs.Random, false) + if err != nil { + log.Error("Failed to create async sealing payload", "err", err) + return nil + } + + resultPayload := catalyst.NewPayload(resCh) + executableData, _ := resultPayload.Resolve() + return executableData +} + +func (s *EthereumService) GetBlockByHash(hash common.Hash) *types.Block { + return s.eth.BlockChain().GetBlockByHash(hash) +} + +func (s *EthereumService) Synced() bool { + return s.eth.Synced() +} diff --git a/builder/eth_service_test.go b/builder/eth_service_test.go new file mode 100644 index 000000000000..a6b6bd32f0fc --- /dev/null +++ b/builder/eth_service_test.go @@ -0,0 +1,99 @@ +package builder + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { + db := rawdb.NewMemoryDatabase() + config := params.AllEthashProtocolChanges + genesis := &core.Genesis{ + Config: config, + Alloc: core.GenesisAlloc{}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + BaseFee: big.NewInt(params.InitialBaseFee), + Difficulty: big.NewInt(0), + } + gblock := genesis.ToBlock() + engine := ethash.NewFaker() + blocks, _ := core.GenerateChain(config, gblock, engine, db, n, nil) + totalDifficulty := big.NewInt(0) + for _, b := range blocks { + totalDifficulty.Add(totalDifficulty, b.Difficulty()) + } + config.TerminalTotalDifficulty = totalDifficulty + return genesis, blocks +} + +// startEthService creates a full node instance for testing. +func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { + t.Helper() + + n, err := node.New(&node.Config{ + P2P: p2p.Config{ + ListenAddr: "0.0.0.0:0", + NoDiscovery: true, + MaxPeers: 25, + }}) + if err != nil { + t.Fatal("can't create node:", err) + } + + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.SnapSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} + ethservice, err := eth.New(n, ethcfg) + if err != nil { + t.Fatal("can't create eth service:", err) + } + if err := n.Start(); err != nil { + t.Fatal("can't start node:", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { + n.Close() + t.Fatal("can't import test blocks:", err) + } + time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event + + ethservice.SetSynced() + return n, ethservice +} + +func TestBuildBlock(t *testing.T) { + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks) + defer n.Close() + + parent := ethservice.BlockChain().CurrentBlock() + + testPayloadAttributes := &BuilderPayloadAttributes{ + Timestamp: hexutil.Uint64(parent.Time() + 1), + Random: common.Hash{0x05, 0x10}, + SuggestedFeeRecipient: common.Address{0x04, 0x10}, + GasLimit: uint64(4800000), + Slot: uint64(25), + } + + service := NewEthereumService(ethservice) + executableData := service.BuildBlock(testPayloadAttributes) + + require.Equal(t, common.Address{0x04, 0x10}, executableData.FeeRecipient) + require.Equal(t, common.Hash{0x05, 0x10}, executableData.Random) + require.Equal(t, parent.Hash(), executableData.ParentHash) + require.Equal(t, parent.Time()+1, executableData.Timestamp) +} diff --git a/builder/local_relay_test.go b/builder/local_relay_test.go index a1a8dad5cc3a..3e706f5bfedc 100644 --- a/builder/local_relay_test.go +++ b/builder/local_relay_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/require" ) -func newTestBackend(t *testing.T) (*Builder, *LocalRelay, *ValidatorPrivateData) { +func newTestBackend(t *testing.T, forkchoiceData *beacon.ExecutableDataV1, block *types.Block) (*Builder, *LocalRelay, *ValidatorPrivateData) { validator := NewRandomValidator() sk, _ := bls.GenerateRandomSecretKey() bDomain := boostTypes.ComputeDomain(boostTypes.DomainTypeAppBuilder, [4]byte{0x02, 0x0, 0x0, 0x0}, boostTypes.Hash{}) @@ -28,7 +28,8 @@ func newTestBackend(t *testing.T) (*Builder, *LocalRelay, *ValidatorPrivateData) cDomain := boostTypes.ComputeDomain(boostTypes.DomainTypeBeaconProposer, [4]byte{0x02, 0x0, 0x0, 0x0}, genesisValidatorsRoot) beaconClient := &testBeaconClient{validator: validator} localRelay := NewLocalRelay(sk, beaconClient, bDomain, cDomain, ForkData{}, true) - backend := NewBuilder(sk, beaconClient, localRelay, bDomain) + ethService := &testEthereumService{synced: true, testExecutableData: forkchoiceData, testBlock: block} + backend := NewBuilder(sk, beaconClient, localRelay, bDomain, ethService) // service := NewService("127.0.0.1:31545", backend) return backend, localRelay, validator @@ -53,7 +54,7 @@ func testRequest(t *testing.T, localRelay *LocalRelay, method string, path strin } func TestValidatorRegistration(t *testing.T) { - _, relay, _ := newTestBackend(t) + _, relay, _ := newTestBackend(t, nil, nil) log.Error("rsk", "sk", hexutil.Encode(relay.relaySecretKey.Serialize())) v := NewRandomValidator() @@ -111,8 +112,6 @@ func registerValidator(t *testing.T, v *ValidatorPrivateData, relay *LocalRelay) } func TestGetHeader(t *testing.T) { - backend, relay, validator := newTestBackend(t) - forkchoiceData := &beacon.ExecutableDataV1{ ParentHash: common.HexToHash("0xafafafa"), FeeRecipient: common.Address{0x01}, @@ -125,6 +124,8 @@ func TestGetHeader(t *testing.T) { Profit: big.NewInt(10), } + backend, relay, validator := newTestBackend(t, forkchoiceData, forkchoiceBlock) + path := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 0, forkchoiceData.ParentHash.Hex(), validator.Pk.String()) rr := testRequest(t, relay, "GET", path, nil) require.Equal(t, `{"code":400,"message":"unknown validator"}`+"\n", rr.Body.String()) @@ -139,7 +140,7 @@ func TestGetHeader(t *testing.T) { require.Equal(t, ``, rr.Body.String()) require.Equal(t, 204, rr.Code) - backend.newSealedBlock(forkchoiceData, forkchoiceBlock, &beacon.PayloadAttributesV1{}) + backend.OnPayloadAttribute(&BuilderPayloadAttributes{}) path = fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 0, forkchoiceData.ParentHash.Hex(), validator.Pk.String()) rr = testRequest(t, relay, "GET", path, nil) @@ -170,8 +171,6 @@ func TestGetHeader(t *testing.T) { } func TestGetPayload(t *testing.T) { - backend, relay, validator := newTestBackend(t) - forkchoiceData := &beacon.ExecutableDataV1{ ParentHash: common.HexToHash("0xafafafa"), FeeRecipient: common.Address{0x01}, @@ -183,8 +182,10 @@ func TestGetPayload(t *testing.T) { Profit: big.NewInt(10), } + backend, relay, validator := newTestBackend(t, forkchoiceData, forkchoiceBlock) + registerValidator(t, validator, relay) - backend.newSealedBlock(forkchoiceData, forkchoiceBlock, &beacon.PayloadAttributesV1{}) + backend.OnPayloadAttribute(&BuilderPayloadAttributes{}) path := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 0, forkchoiceData.ParentHash.Hex(), validator.Pk.String()) rr := testRequest(t, relay, "GET", path, nil) diff --git a/builder/service.go b/builder/service.go index f934d5e31c79..f33b2d4e45ea 100644 --- a/builder/service.go +++ b/builder/service.go @@ -10,6 +10,7 @@ import ( "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/gorilla/mux" "github.com/flashbots/go-boost-utils/bls" @@ -25,8 +26,18 @@ const ( _PathGetPayload = "/eth/v1/builder/blinded_blocks" ) +type BuilderPayloadAttributes struct { + Timestamp hexutil.Uint64 `json:"timestamp"` + Random common.Hash `json:"prevRandao"` + SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient,omitempty"` + Slot uint64 `json:"slot"` + HeadHash common.Hash `json:"blockHash"` + GasLimit uint64 +} + type Service struct { - srv *http.Server + srv *http.Server + builder IBuilder } func (s *Service) Start() { @@ -34,6 +45,10 @@ func (s *Service) Start() { go s.srv.ListenAndServe() } +func (s *Service) PayloadAttributes(payloadAttributes *BuilderPayloadAttributes) error { + return s.builder.OnPayloadAttribute(payloadAttributes) +} + func getRouter(localRelay *LocalRelay) http.Handler { router := mux.NewRouter() @@ -49,18 +64,19 @@ func getRouter(localRelay *LocalRelay) http.Handler { return loggedRouter } -func NewService(listenAddr string, localRelay *LocalRelay) *Service { +func NewService(listenAddr string, localRelay *LocalRelay, builder *Builder) *Service { return &Service{ srv: &http.Server{ Addr: listenAddr, Handler: getRouter(localRelay), /* - ReadTimeout: - ReadHeaderTimeout: - WriteTimeout: - IdleTimeout: + ReadTimeout: + ReadHeaderTimeout: + WriteTimeout: + IdleTimeout: */ }, + builder: builder, } } @@ -116,8 +132,7 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *BuilderConfig) error copy(bellatrixForkVersion[:], bellatrixForkVersionBytes[:4]) proposerSigningDomain := boostTypes.ComputeDomain(boostTypes.DomainTypeBeaconProposer, bellatrixForkVersion, genesisValidatorsRoot) - var beaconClient IBeaconClient - beaconClient = NewBeaconClient(cfg.BeaconEndpoint) + beaconClient := NewBeaconClient(cfg.BeaconEndpoint) localRelay := NewLocalRelay(relaySk, beaconClient, builderSigningDomain, proposerSigningDomain, ForkData{cfg.GenesisForkVersion, cfg.BellatrixForkVersion, cfg.GenesisValidatorsRoot}, cfg.EnableValidatorChecks) @@ -128,11 +143,20 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *BuilderConfig) error relay = localRelay } - builderBackend := NewBuilder(builderSk, beaconClient, relay, builderSigningDomain) - builderService := NewService(cfg.ListenAddr, localRelay) + ethereumService := NewEthereumService(backend) + + builderBackend := NewBuilder(builderSk, beaconClient, relay, builderSigningDomain, ethereumService) + builderService := NewService(cfg.ListenAddr, localRelay, builderBackend) builderService.Start() - backend.SetSealedBlockHook(builderBackend.newSealedBlock) - backend.SetForkchoiceHook(builderBackend.onForkchoice) + stack.RegisterAPIs([]rpc.API{ + { + Namespace: "builder", + Version: "1.0", + Service: builderService, + Public: true, + Authenticated: true, + }, + }) return nil } diff --git a/eth/backend.go b/eth/backend.go index 3dfb48786ab2..6368c0e03c56 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/core" - beaconTypes "github.com/ethereum/go-ethereum/core/beacon" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/pruner" @@ -86,9 +85,6 @@ type Ethereum struct { bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports closeBloomHandler chan struct{} - newSealedBlockHook func(*beaconTypes.ExecutableDataV1, *types.Block, *beaconTypes.PayloadAttributesV1) - forkchoiceHook func(*beaconTypes.PayloadAttributesV1) - APIBackend *EthAPIBackend miner *miner.Miner @@ -336,27 +332,6 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") } -func (s *Ethereum) SetSealedBlockHook(newSealedBlockHook func(*beaconTypes.ExecutableDataV1, *types.Block, *beaconTypes.PayloadAttributesV1)) { - s.newSealedBlockHook = newSealedBlockHook -} - -func (s *Ethereum) NewSealedBlock(data *beaconTypes.ExecutableDataV1, block *types.Block, payloadAttributes *beaconTypes.PayloadAttributesV1) { - if s.newSealedBlockHook != nil { - s.newSealedBlockHook(data, block, payloadAttributes) - } -} - -func (s *Ethereum) SetForkchoiceHook(forkchoiceHook func(*beaconTypes.PayloadAttributesV1)) { - s.forkchoiceHook = forkchoiceHook -} - -func (s *Ethereum) ForkchoiceHook(payloadAttributes *beaconTypes.PayloadAttributesV1) { - // Possibly modifies payloadAttributes's fee recipient - if s.forkchoiceHook != nil { - s.forkchoiceHook(payloadAttributes) - } -} - // isLocalBlock checks whether the specified block is mined // by local miner accounts. // diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ebc2c5c798db..7019daf9b28f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -204,11 +204,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl api.forkchoiceLock.Lock() defer api.forkchoiceLock.Unlock() - log.Info("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash) - - // Adjusts payload attributes to next slot's validator preferences - api.eth.ForkchoiceHook(payloadAttributes) - + log.Trace("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash) if update.HeadBlockHash == (common.Hash{}) { log.Warn("Forkchoice requested update to zero hash") return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this? diff --git a/node/defaults.go b/node/defaults.go index fd0277e29dc9..0b254fcb3c8a 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -43,7 +43,7 @@ var ( DefaultAuthVhosts = []string{"localhost"} // Default virtual hosts for the authenticated apis DefaultAuthOrigins = []string{"localhost"} // Default origins for the authenticated apis DefaultAuthPrefix = "" // Default prefix for the authenticated apis - DefaultAuthModules = []string{"eth", "engine"} + DefaultAuthModules = []string{"eth", "engine", "builder"} ) // DefaultConfig contains reasonable default settings. diff --git a/node/endpoints.go b/node/endpoints.go index 14c12fd1f175..0f33d4be06e0 100644 --- a/node/endpoints.go +++ b/node/endpoints.go @@ -62,7 +62,7 @@ func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available [ } for _, name := range modules { if _, ok := availableSet[name]; !ok { - if name != rpc.MetadataApi && name != rpc.EngineApi { + if name != rpc.MetadataApi && name != rpc.EngineApi && name != rpc.BuilderApi { bad = append(bad, name) } } diff --git a/rpc/server.go b/rpc/server.go index 9c72c26d7b94..b2d9503730a6 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -27,6 +27,7 @@ import ( const MetadataApi = "rpc" const EngineApi = "engine" +const BuilderApi = "builder" // CodecOption specifies which type of messages a codec supports. //