From e91a982742220b8fec056a8b271ee948f00728f0 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Thu, 31 Aug 2023 23:20:55 +0530 Subject: [PATCH 01/19] Add support to recognize proposer_commitments for pepc-boost --- builder/builder.go | 9 +++++---- builder/builder_test.go | 7 ++++--- builder/eth_service.go | 18 +++++++++++------- builder/eth_service_test.go | 26 ++++++++++++++++++++++++++ builder/relay.go | 16 +++++++++------- core/types/builder.go | 4 +++- miner/multi_worker.go | 4 ++++ miner/payload_building.go | 15 ++++++++------- 8 files changed, 70 insertions(+), 29 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 44201bcdeb..c25027804d 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -40,9 +40,10 @@ const ( type PubkeyHex string type ValidatorData struct { - Pubkey PubkeyHex - FeeRecipient bellatrix.ExecutionAddress - GasLimit uint64 + Pubkey PubkeyHex + FeeRecipient bellatrix.ExecutionAddress + GasLimit uint64 + ProposerCommitment uint64 } type IRelay interface { @@ -413,7 +414,7 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. queueBestEntry blockQueueEntry ) - log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp)) + log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp), "proposerCommitment", attrs.ProposerCommitment, "gasLimit", attrs.GasLimit) submitBestBlock := func() { queueMu.Lock() diff --git a/builder/builder_test.go b/builder/builder_test.go index 7c8c4cca9e..3f132d7be2 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -36,9 +36,10 @@ func TestOnPayloadAttributes(t *testing.T) { feeRecipient, _ := utils.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc00") testRelay := testRelay{ gvsVd: ValidatorData{ - Pubkey: PubkeyHex(testBeacon.validator.Pk.String()), - FeeRecipient: feeRecipient, - GasLimit: 10, + Pubkey: PubkeyHex(testBeacon.validator.Pk.String()), + FeeRecipient: feeRecipient, + GasLimit: 10, + ProposerCommitment: 1, }, } diff --git a/builder/eth_service.go b/builder/eth_service.go index 26e3cf9379..e4a137cc78 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -32,6 +32,9 @@ type testEthereumService struct { } func (t *testEthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { + if attrs.ProposerCommitment > 0 { + return errors.New("proposeCommitment is not supported in test ethereum service") + } sealedBlockCallback(t.testBlock, t.testBlockValue, time.Now(), t.testBundlesMerged, t.testAllBundles, t.testUsedSbundles) return nil } @@ -55,13 +58,14 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal // Send a request to generate a full block in the background. // The result can be obtained via the returned channel. args := &miner.BuildPayloadArgs{ - Parent: attrs.HeadHash, - Timestamp: uint64(attrs.Timestamp), - FeeRecipient: attrs.SuggestedFeeRecipient, - GasLimit: attrs.GasLimit, - Random: attrs.Random, - Withdrawals: attrs.Withdrawals, - BlockHook: sealedBlockCallback, + Parent: attrs.HeadHash, + Timestamp: uint64(attrs.Timestamp), + FeeRecipient: attrs.SuggestedFeeRecipient, + GasLimit: attrs.GasLimit, + Random: attrs.Random, + Withdrawals: attrs.Withdrawals, + BlockHook: sealedBlockCallback, + ProposerCommitment: attrs.ProposerCommitment, } payload, err := s.eth.Miner().BuildPayload(args) diff --git a/builder/eth_service_test.go b/builder/eth_service_test.go index b1f3dff65c..79ff05183a 100644 --- a/builder/eth_service_test.go +++ b/builder/eth_service_test.go @@ -76,6 +76,31 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) return n, ethservice } +func TestBuildBlockUnsupportedProposerCommitment(t *testing.T) { + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks) + defer n.Close() + + parent := ethservice.BlockChain().CurrentBlock() + + testPayloadAttributes := &types.BuilderPayloadAttributes{ + Timestamp: hexutil.Uint64(parent.Time + 1), + Random: common.Hash{0x05, 0x10}, + SuggestedFeeRecipient: common.Address{0x04, 0x10}, + GasLimit: uint64(4800000), + Slot: uint64(25), + ProposerCommitment: 1, + } + + service := NewEthereumService(ethservice) + service.eth.APIBackend.Miner().SetEtherbase(common.Address{0x05, 0x11}) + + err := service.BuildBlock(testPayloadAttributes, func(block *types.Block, blockValue *big.Int, _ time.Time, _, _ []types.SimulatedBundle, _ []types.UsedSBundle) { + }) + + require.ErrorContainsf(t, err, "TOB_ROB_SPLIT not supported yet", "expected error") +} + func TestBuildBlock(t *testing.T) { genesis, blocks := generatePreMergeChain(10) n, ethservice := startEthService(t, genesis, blocks) @@ -89,6 +114,7 @@ func TestBuildBlock(t *testing.T) { SuggestedFeeRecipient: common.Address{0x04, 0x10}, GasLimit: uint64(4800000), Slot: uint64(25), + ProposerCommitment: 0, } service := NewEthereumService(ethservice) diff --git a/builder/relay.go b/builder/relay.go index a28fe1e71c..4afbc60eec 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -53,10 +53,11 @@ type GetValidatorRelayResponse []struct { Slot uint64 `json:"slot,string"` Entry struct { Message struct { - FeeRecipient string `json:"fee_recipient"` - GasLimit uint64 `json:"gas_limit,string"` - Timestamp uint64 `json:"timestamp,string"` - Pubkey string `json:"pubkey"` + FeeRecipient string `json:"fee_recipient"` + GasLimit uint64 `json:"gas_limit,string"` + Timestamp uint64 `json:"timestamp,string"` + Pubkey string `json:"pubkey"` + ProposerCommitment uint64 `json:"proposer_commitment,string"` } `json:"message"` Signature string `json:"signature"` } `json:"entry"` @@ -215,9 +216,10 @@ func (r *RemoteRelay) getSlotValidatorMapFromRelay() (map[uint64]ValidatorData, pubkeyHex := PubkeyHex(strings.ToLower(data.Entry.Message.Pubkey)) res[data.Slot] = ValidatorData{ - Pubkey: pubkeyHex, - FeeRecipient: feeRecipient, - GasLimit: data.Entry.Message.GasLimit, + Pubkey: pubkeyHex, + FeeRecipient: feeRecipient, + GasLimit: data.Entry.Message.GasLimit, + ProposerCommitment: data.Entry.Message.ProposerCommitment, } } diff --git a/core/types/builder.go b/core/types/builder.go index d31da82fdb..5a595de4a8 100644 --- a/core/types/builder.go +++ b/core/types/builder.go @@ -14,6 +14,7 @@ type BuilderPayloadAttributes struct { HeadHash common.Hash `json:"blockHash"` Withdrawals Withdrawals `json:"withdrawals"` GasLimit uint64 + ProposerCommitment uint64 `json:"proposerCommitment"` } func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bool { @@ -22,7 +23,8 @@ func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bo attrs.SuggestedFeeRecipient != other.SuggestedFeeRecipient || attrs.Slot != other.Slot || attrs.HeadHash != other.HeadHash || - attrs.GasLimit != other.GasLimit { + attrs.GasLimit != other.GasLimit || + attrs.ProposerCommitment != other.ProposerCommitment { return false } diff --git a/miner/multi_worker.go b/miner/multi_worker.go index 93cb8aadae..830c5d9361 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -87,6 +87,10 @@ func (w *multiWorker) disablePreseal() { } func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { + if args.ProposerCommitment != 1 { + log.Error("TOB_ROB_SPLIT not supported yet") + return nil, errors.New("TOB_ROB_SPLIT not supported yet") + } // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. diff --git a/miner/payload_building.go b/miner/payload_building.go index 5ed15c1dd3..332425ff86 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -35,13 +35,14 @@ import ( // Check engine-api specification for more details. // https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1 type BuildPayloadArgs struct { - Parent common.Hash // The parent block to build payload on top - Timestamp uint64 // The provided timestamp of generated payload - FeeRecipient common.Address // The provided recipient address for collecting transaction fee - GasLimit uint64 - Random common.Hash // The provided randomness value - Withdrawals types.Withdrawals // The provided withdrawals - BlockHook BlockHookFn + Parent common.Hash // The parent block to build payload on top + Timestamp uint64 // The provided timestamp of generated payload + FeeRecipient common.Address // The provided recipient address for collecting transaction fee + GasLimit uint64 + Random common.Hash // The provided randomness value + Withdrawals types.Withdrawals // The provided withdrawals + BlockHook BlockHookFn + ProposerCommitment uint64 // The provided proposer commitment } // Id computes an 8-byte identifier by hashing the components of the payload arguments. From 9151bafe52bf8f821513c9381070f85ca2be360d Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 5 Sep 2023 13:02:47 +0530 Subject: [PATCH 02/19] pepc-boost prototype --- build.sh | 3 + builder/builder.go | 107 +++++++-- builder/eth_service.go | 33 ++- builder/local_relay.go | 20 ++ builder/relay.go | 110 ++++++++- builder/relay_aggregator.go | 20 ++ builder/resubmit_utils.go | 3 +- core/types/transaction.go | 8 + eth/block-validation/api.go | 109 +++++++++ eth/block-validation/api_test.go | 13 + miner/contract_simulator_test.go | 2 +- miner/miner.go | 12 + miner/multi_worker.go | 110 ++++++++- miner/payload_building.go | 8 +- miner/verify_bundles.go | 4 + miner/worker.go | 394 +++++++++++++++++++++++++++---- miner/worker_test.go | 6 +- 17 files changed, 877 insertions(+), 85 deletions(-) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..c56d158d8a --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +docker build -t pepc-boost-builder .; +docker tag pepc-boost-builder:latest public.ecr.aws/t1d5h1w5/pepc-boost-builder:latest; +docker push public.ecr.aws/t1d5h1w5/pepc-boost-builder:latest; \ No newline at end of file diff --git a/builder/builder.go b/builder/builder.go index c25027804d..2e211ffd34 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -48,7 +48,11 @@ type ValidatorData struct { type IRelay interface { SubmitBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error + SubmitTobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error + SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error + SubmitTobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error + SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) Config() RelayConfig Start() error @@ -203,9 +207,10 @@ func (b *Builder) Stop() error { func (b *Builder) onSealedBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isTobBlock bool, isRobBlock bool) error { if b.eth.Config().IsShanghai(block.Time()) { - if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs); err != nil { + log.Info("DEBUG: Submitting capella block\n") + if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs, isTobBlock, isRobBlock); err != nil { return err } } else { @@ -281,7 +286,7 @@ func (b *Builder) submitBellatrixBlock(block *types.Block, blockValue *big.Int, func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isTobBlock bool, isRobBlock bool) error { executableData := engine.BlockToExecutableData(block, blockValue) payload, err := executableDataToCapellaExecutionPayload(executableData.ExecutionPayload) if err != nil { @@ -326,10 +331,26 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or } } else { go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg) - err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) - if err != nil { - log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) - return err + if isTobBlock { + log.Info("DEBUG: submitting tob capella block") + err = b.relay.SubmitTobBlockCapella(&blockSubmitReq, vd) + if err != nil { + log.Error("could not submit tob capella block", "err", err, "#commitedBundles", len(commitedBundles)) + return err + } + } else if isRobBlock { + log.Info("DEBUG: submitting rob capella block") + err = b.relay.SubmitRobBlockCapella(&blockSubmitReq, vd) + if err != nil { + log.Error("could not submit rob capella block", "err", err, "#commitedBundles", len(commitedBundles)) + return err + } + } else { + err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) + if err != nil { + log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) + return err + } } } @@ -347,8 +368,10 @@ func (b *Builder) OnPayloadAttribute(attrs *types.BuilderPayloadAttributes) erro return fmt.Errorf("could not get validator while submitting block for slot %d - %w", attrs.Slot, err) } + log.Info("DEBUG: validator info is", "vd", vd) attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) attrs.GasLimit = vd.GasLimit + attrs.ProposerCommitment = vd.ProposerCommitment proposerPubkey, err := utils.HexToPubkey(string(vd.Pubkey)) if err != nil { @@ -409,18 +432,27 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. var ( queueSignal = make(chan struct{}, 1) - queueMu sync.Mutex - queueLastSubmittedHash common.Hash - queueBestEntry blockQueueEntry + tobQueueMu sync.Mutex + queueMu sync.Mutex + queueLastSubmittedTobHash common.Hash + queueLastSubmittedHash common.Hash + queueBestTobEntry blockQueueEntry + queueBestEntry blockQueueEntry ) log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp), "proposerCommitment", attrs.ProposerCommitment, "gasLimit", attrs.GasLimit) submitBestBlock := func() { + log.Info("DEBUG: In submit best block!") + isRobBlock := false + if attrs.ProposerCommitment == 2 { + isRobBlock = true + } + log.Info("DEBUG: Submitting ROB block!!", "isRobBlock", isRobBlock) queueMu.Lock() - if queueBestEntry.block.Hash() != queueLastSubmittedHash { + if queueBestEntry.block != nil && queueBestEntry.block.Hash() != queueLastSubmittedHash { err := b.onSealedBlock(queueBestEntry.block, queueBestEntry.blockValue, queueBestEntry.ordersCloseTime, queueBestEntry.sealedAt, - queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs) + queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs, false, isRobBlock) if err != nil { log.Error("could not run sealed block hook", "err", err) @@ -431,13 +463,30 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. queueMu.Unlock() } + submitBestTobBlock := func() { + log.Info("DEBUG: In submitBestTobBlock\n") + tobQueueMu.Lock() + if queueBestTobEntry.block != nil && queueBestTobEntry.block.Hash() != queueLastSubmittedTobHash { + log.Info("DEBUG: Running onSealedBlock in submitBestTobBlock!!\n") + err := b.onSealedBlock(queueBestTobEntry.block, queueBestTobEntry.blockValue, queueBestTobEntry.ordersCloseTime, queueBestTobEntry.sealedAt, + queueBestTobEntry.commitedBundles, queueBestTobEntry.allBundles, queueBestTobEntry.usedSbundles, proposerPubkey, vd, attrs, true, false) + + if err != nil { + log.Error("could not run sealed tob block hook", "err", err) + } else { + queueLastSubmittedTobHash = queueBestTobEntry.block.Hash() + } + } + tobQueueMu.Unlock() + } + // Avoid submitting early into a given slot. For example if slots have 12 second interval, submissions should // not begin until 8 seconds into the slot. slotTime := time.Unix(int64(attrs.Timestamp), 0).UTC() slotSubmitStartTime := slotTime.Add(-b.submissionOffsetFromEndOfSlot) // Empties queue, submits the best block for current job with rate limit (global for all jobs) - go runResubmitLoop(ctx, b.limiter, queueSignal, submitBestBlock, slotSubmitStartTime) + go runResubmitLoop(ctx, b.limiter, queueSignal, submitBestBlock, submitBestTobBlock, slotSubmitStartTime) // Populates queue with submissions that increase block profit blockHook := func(block *types.Block, blockValue *big.Int, ordersCloseTime time.Time, @@ -447,6 +496,7 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. return } + log.Info("DEBUG: Running ROB block hook\n") sealedAt := time.Now() queueMu.Lock() @@ -469,13 +519,42 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. } } + // Populates queue with submissions that increase block profit + tobBlockHook := func(block *types.Block, blockValue *big.Int) { + if ctx.Err() != nil { + return + } + + log.Info("DEBUG: Running TOB Block hook!!!!\n") + sealedAt := time.Now() + + tobQueueMu.Lock() + defer tobQueueMu.Unlock() + if block.Hash() != queueLastSubmittedTobHash { + queueBestTobEntry = blockQueueEntry{ + block: block, + blockValue: new(big.Int).Set(blockValue), + ordersCloseTime: time.Time{}, + sealedAt: sealedAt, + commitedBundles: []types.SimulatedBundle{}, + allBundles: []types.SimulatedBundle{}, + usedSbundles: []types.UsedSBundle{}, + } + + select { + case queueSignal <- struct{}{}: + default: + } + } + } + // resubmits block builder requests every builderBlockResubmitInterval runRetryLoop(ctx, b.builderResubmitInterval, func() { log.Debug("retrying BuildBlock", "slot", attrs.Slot, "parent", attrs.HeadHash, "resubmit-interval", b.builderResubmitInterval.String()) - err := b.eth.BuildBlock(attrs, blockHook) + err := b.eth.BuildBlock(attrs, blockHook, tobBlockHook) if err != nil { log.Warn("Failed to build block", "err", err) } diff --git a/builder/eth_service.go b/builder/eth_service.go index e4a137cc78..d2bc79ab7c 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -15,7 +15,7 @@ import ( ) type IEthereumService interface { - BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error + BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error GetBlockByHash(hash common.Hash) *types.Block Config() *params.ChainConfig Synced() bool @@ -31,7 +31,7 @@ type testEthereumService struct { testUsedSbundles []types.UsedSBundle } -func (t *testEthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { +func (t *testEthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error { if attrs.ProposerCommitment > 0 { return errors.New("proposeCommitment is not supported in test ethereum service") } @@ -54,9 +54,10 @@ func NewEthereumService(eth *eth.Ethereum) *EthereumService { } // TODO: we should move to a setup similar to catalyst local blocks & payload ids -func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { +func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error { // Send a request to generate a full block in the background. // The result can be obtained via the returned channel. + log.Info("DEBUG: In build block!!") args := &miner.BuildPayloadArgs{ Parent: attrs.HeadHash, Timestamp: uint64(attrs.Timestamp), @@ -65,9 +66,26 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal Random: attrs.Random, Withdrawals: attrs.Withdrawals, BlockHook: sealedBlockCallback, + TobBlockHook: sealedTobBlockCallback, ProposerCommitment: attrs.ProposerCommitment, } + log.Info("DEBUG: Args for build block!!", "args", args) + + tobResCh := make(chan *engine.ExecutionPayloadEnvelope, 1) + if attrs.ProposerCommitment == 2 { + log.Info("DEBUG: Starting to Build TOB Payload!!!") + tobPayload, err := s.eth.Miner().BuildTobPayload(args) + if err != nil { + log.Error("Failed to build tob payload", "err", err) + return err + } + + go func() { + tobResCh <- tobPayload.ResolveFull() + }() + } + log.Info("DEBUG: Starting to build ROB payload!!") payload, err := s.eth.Miner().BuildPayload(args) if err != nil { log.Error("Failed to build payload", "err", err) @@ -85,14 +103,19 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal select { case payload := <-resCh: if payload == nil { - return errors.New("received nil payload from sealing work") + return errors.New("received nil tob payload from sealing work") + } + case tobPayload := <-tobResCh: + if tobPayload == nil { + return errors.New("received nil tob payload from sealing work") } - return nil case <-timer.C: payload.Cancel() log.Error("timeout waiting for block", "parent hash", attrs.HeadHash, "slot", attrs.Slot) return errors.New("timeout waiting for block result") } + + return nil } func (s *EthereumService) GetBlockByHash(hash common.Hash) *types.Block { diff --git a/builder/local_relay.go b/builder/local_relay.go index 9c2abef78c..9a099b0ded 100644 --- a/builder/local_relay.go +++ b/builder/local_relay.go @@ -66,6 +66,26 @@ type LocalRelay struct { fd ForkData } +func (r *LocalRelay) SubmitTobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *LocalRelay) SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *LocalRelay) SubmitTobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *LocalRelay) SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + func NewLocalRelay(sk *bls.SecretKey, beaconClient IBeaconClient, builderSigningDomain, proposerSigningDomain phase0.Domain, fd ForkData, enableBeaconChecks bool) (*LocalRelay, error) { blsPk, err := bls.PublicKeyFromSecretKey(sk) if err != nil { diff --git a/builder/relay.go b/builder/relay.go index 4afbc60eec..be9f61b9bb 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -72,7 +72,7 @@ func (r *RemoteRelay) updateValidatorsMap(currentSlot uint64, retries int) error r.validatorSyncOngoing = true r.validatorsLock.Unlock() - log.Info("requesting ", "currentSlot", currentSlot) + //log.Info("DEBUG: requesting from relayer", "currentSlot", currentSlot) newMap, err := r.getSlotValidatorMapFromRelay() for err != nil && retries > 0 { log.Error("could not get validators map from relay, retrying", "err", err) @@ -100,10 +100,12 @@ func (r *RemoteRelay) GetValidatorForSlot(nextSlot uint64) (ValidatorData, error // next slot is expected to be the actual chain's next slot, not something requested by the user! // if not sanitized it will force resync of validator data and possibly is a DoS vector + //log.Info("DEBUG: Getting validator for slot", "slot", nextSlot) r.validatorsLock.RLock() if r.lastRequestedSlot == 0 || nextSlot/32 > r.lastRequestedSlot/32 { // Every epoch request validators map go func() { + //log.Info("DEBUG: Updating validators map", "slot", nextSlot) err := r.updateValidatorsMap(nextSlot, 1) if err != nil { log.Error("could not update validators map", "err", err) @@ -156,6 +158,42 @@ func (r *RemoteRelay) SubmitBlock(msg *bellatrix.SubmitBlockRequest, _ Validator return nil } +func (r *RemoteRelay) SubmitTobBlock(msg *bellatrix.SubmitBlockRequest, _ ValidatorData) error { + log.Info("submitting tob block to remote relay", "endpoint", r.config.Endpoint) + endpoint := r.config.Endpoint + "/relay/v1/builder/tob_blocks" + code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + + if r.localRelay != nil { + r.localRelay.submitBlock(msg) + } + + return nil +} + +func (r *RemoteRelay) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, _ ValidatorData) error { + log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) + endpoint := r.config.Endpoint + "/relay/v1/builder/rob_blocks" + code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + + if r.localRelay != nil { + r.localRelay.submitBlock(msg) + } + + return nil +} + func (r *RemoteRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { log.Info("submitting block to remote relay", "endpoint", r.config.Endpoint) @@ -194,6 +232,76 @@ func (r *RemoteRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, _ Vali return nil } +func (r *RemoteRelay) SubmitTobBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { + log.Info("submitting block to remote relay", "endpoint", r.config.Endpoint) + + endpoint := r.config.Endpoint + "/relay/v1/builder/tob_blocks" + + if r.config.SszEnabled { + bodyBytes, err := msg.MarshalSSZ() + if err != nil { + return fmt.Errorf("error marshaling ssz: %w", err) + } + log.Debug("submitting block to remote relay", "endpoint", r.config.Endpoint) + code, err := SendSSZRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, bodyBytes, r.config.GzipEnabled) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + } else { + code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + } + + if r.localRelay != nil { + r.localRelay.submitBlockCapella(msg) + } + + return nil +} + +func (r *RemoteRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { + log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) + + endpoint := r.config.Endpoint + "/relay/v1/builder/rob_blocks" + + if r.config.SszEnabled { + bodyBytes, err := msg.MarshalSSZ() + if err != nil { + return fmt.Errorf("error marshaling ssz: %w", err) + } + log.Debug("submitting block to remote relay", "endpoint", r.config.Endpoint) + code, err := SendSSZRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, bodyBytes, r.config.GzipEnabled) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + } else { + code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) + if err != nil { + return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + } + + if r.localRelay != nil { + r.localRelay.submitBlockCapella(msg) + } + + return nil +} + func (r *RemoteRelay) getSlotValidatorMapFromRelay() (map[uint64]ValidatorData, error) { var dst GetValidatorRelayResponse code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, r.config.Endpoint+"/relay/v1/builder/validators", nil, &dst) diff --git a/builder/relay_aggregator.go b/builder/relay_aggregator.go index 12f6f62c13..2385042c7d 100644 --- a/builder/relay_aggregator.go +++ b/builder/relay_aggregator.go @@ -18,6 +18,26 @@ type RemoteRelayAggregator struct { registrationsCache map[ValidatorData][]IRelay } +func (r *RemoteRelayAggregator) SubmitTobBlock(msg *bellatrix.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *RemoteRelayAggregator) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *RemoteRelayAggregator) SubmitTobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + +func (r *RemoteRelayAggregator) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { + //TODO implement me + panic("implement me") +} + func NewRemoteRelayAggregator(primary IRelay, secondary []IRelay) *RemoteRelayAggregator { relays := []IRelay{primary} return &RemoteRelayAggregator{ diff --git a/builder/resubmit_utils.go b/builder/resubmit_utils.go index 4819287e6d..cceb9951aa 100644 --- a/builder/resubmit_utils.go +++ b/builder/resubmit_utils.go @@ -9,7 +9,7 @@ import ( ) // runResubmitLoop checks for update signal and calls submit respecting provided rate limiter and context -func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <-chan struct{}, submit func(), submitTime time.Time) { +func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <-chan struct{}, submit func(), submitTob func(), submitTime time.Time) { if submitTime.IsZero() { log.Warn("skipping resubmit loop - zero submit time found") return @@ -62,6 +62,7 @@ func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <- delay := res.Delay() if delay == 0 { submit() + submitTob() continue } diff --git a/core/types/transaction.go b/core/types/transaction.go index 03eb018326..eda68bbb3b 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -413,6 +413,14 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e // Transactions implements DerivableList for transactions. type Transactions []*Transaction +func (s Transactions) Index(i int) *Transaction { + return s[i] +} + +func (s Transactions) Append(tx *Transaction) Transactions { + return append(s, tx) +} + // Len returns the length of s. func (s Transactions) Len() int { return len(s) } diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index a51b7504b5..2a23da9e46 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -6,9 +6,11 @@ import ( "fmt" "math/big" "os" + "sort" bellatrixapi "github.com/attestantio/go-builder-client/api/bellatrix" capellaapi "github.com/attestantio/go-builder-client/api/capella" + "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -17,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" ) @@ -228,6 +231,112 @@ func (r *BuilderBlockValidationRequestV2) UnmarshalJSON(data []byte) error { return nil } +type BlockAssemblerRequest struct { + TobPayload *capella.ExecutionPayload `json:"tob_payload"` + TobPayloadBidValue *big.Int `json:"tob_payload_bid_value,string"` + RobPayload *capella.ExecutionPayload `json:"rob_payload"` + RobPayloadBidValue *big.Int `json:"rob_payload_bid_value,string"` +} + +func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*engine.ExecutionPayloadEnvelope, error) { + if params.TobPayload == nil { + return nil, errors.New("nil tob payload") + } + if params.RobPayload == nil { + return nil, errors.New("nil rob payload") + } + if params.TobPayload.ParentHash != params.RobPayload.ParentHash { + return nil, errors.New("tob and rob payloads have different parent hashes") + } + + tobBlock, err := engine.ExecutionPayloadV2ToBlock(params.TobPayload) + if err != nil { + return nil, err + } + robBlock, err := engine.ExecutionPayloadV2ToBlock(params.RobPayload) + if err != nil { + return nil, err + } + + if params.TobPayload.ParentHash != phase0.Hash32(tobBlock.ParentHash()) { + return nil, fmt.Errorf("incorrect ParentHash %s, expected %s", params.TobPayload.ParentHash.String(), tobBlock.ParentHash().String()) + } + if params.TobPayload.BlockHash != phase0.Hash32(tobBlock.Hash()) { + return nil, fmt.Errorf("incorrect BlockHash %s, expected %s", params.TobPayload.BlockHash.String(), tobBlock.Hash().String()) + } + if params.RobPayload.ParentHash != phase0.Hash32(robBlock.Hash()) { + return nil, fmt.Errorf("incorrect ParentHash %s, expected %s", params.RobPayload.BlockHash.String(), robBlock.Hash().String()) + } + if params.RobPayload.BlockHash != phase0.Hash32(robBlock.Hash()) { + return nil, fmt.Errorf("incorrect BlockHash %s, expected %s", params.RobPayload.BlockHash.String(), robBlock.Hash().String()) + } + + // TODO - check for gas limits + // TODO - we still need to support validator payments + + // txs sorted by nonce per address + txMap := make(map[common.Address]types.Transactions) + seenTxs := make(map[common.Hash]struct{}) + assemblerSigner := types.LatestSigner(api.eth.BlockChain().Config()) + for _, tx := range tobBlock.Transactions() { + from, err := types.Sender(assemblerSigner, tx) + if err != nil { + return nil, err + } + txMap[from] = txMap[from].Append(tx) + // check for duplicate txs + // if we see duplicate tx just ignore it, its already added + if _, ok := seenTxs[tx.Hash()]; ok { + continue + } else { + seenTxs[tx.Hash()] = struct{}{} + } + } + for _, tx := range robBlock.Transactions() { + from, err := types.Sender(assemblerSigner, tx) + if err != nil { + return nil, err + } + + txMap[from] = txMap[from].Append(tx) + // check for duplicate txs + // if we see duplicate tx just ignore it, its already added + if _, ok := seenTxs[tx.Hash()]; ok { + continue + } else { + seenTxs[tx.Hash()] = struct{}{} + } + } + // sort each address's txs by nonce + for _, txs := range txMap { + sort.Slice(txs, func(i, j int) bool { + return txs.Index(i).Nonce() < txs.Index(j).Nonce() + }) + } + + log.Info("DEBUG PAYLOAD ASSEMBLER: Txmap is ", "txmap", txMap) + // assemble the txs in map[sender]txs format and pass it in the BuildPayload call + block, err := api.eth.Miner().PayloadAssembler(&miner.BuildPayloadArgs{ + Parent: common.Hash(params.TobPayload.ParentHash), + Timestamp: params.TobPayload.Timestamp, + // TODO - this should be relayer fee recipient + FeeRecipient: common.Address(params.TobPayload.FeeRecipient), + GasLimit: params.RobPayload.GasLimit, + Random: params.TobPayload.PrevRandao, + Withdrawals: nil, + BlockHook: nil, + TobBlockHook: nil, + ProposerCommitment: 2, + AssemblerTxs: txMap, + }) + if err != nil { + return nil, err + } + resolvedBlock := block.ResolveFull() + + return resolvedBlock, nil +} + func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockValidationRequestV2) error { // TODO: fuzztest, make sure the validation is sound // TODO: handle context! diff --git a/eth/block-validation/api_test.go b/eth/block-validation/api_test.go index f7cd3d0b90..dc8f737704 100644 --- a/eth/block-validation/api_test.go +++ b/eth/block-validation/api_test.go @@ -720,6 +720,19 @@ func TestValidateBuilderSubmissionV2_CoinbasePaymentDefault(t *testing.T) { require.ErrorContains(t, api.ValidateBuilderSubmissionV2(req), "payment") } +//func TestBlockAssembler(t *testing.T) { +// genesis, preMergeBlocks := generatePreMergeChain(20) +// lastBlock := preMergeBlocks[len(preMergeBlocks)-1] +// time := lastBlock.Time() + 5 +// genesis.Config.ShanghaiTime = &time +// n, ethservice := startEthService(t, genesis, preMergeBlocks) +// ethservice.Merger().ReachTTD() +// defer n.Close() +// +// apiNoBlock := NewBlockValidationAPI(ethservice, nil, false) +// baseFee := misc.CalcBaseFee(ethservice.BlockChain().Config(), lastBlock.Header()) +//} + func TestValidateBuilderSubmissionV2_Blocklist(t *testing.T) { genesis, preMergeBlocks := generatePreMergeChain(20) lastBlock := preMergeBlocks[len(preMergeBlocks)-1] diff --git a/miner/contract_simulator_test.go b/miner/contract_simulator_test.go index 973053fb83..dca1f75068 100644 --- a/miner/contract_simulator_test.go +++ b/miner/contract_simulator_test.go @@ -201,7 +201,7 @@ func TestSimulatorState(t *testing.T) { require.NoError(t, err) } - block, _, err := w.getSealingBlock(b.chain.CurrentBlock().Hash(), b.chain.CurrentHeader().Time+12, testAddress1, 0, common.Hash{}, nil, false, nil) + block, _, err := w.getSealingBlock(b.chain.CurrentBlock().Hash(), b.chain.CurrentHeader().Time+12, testAddress1, 0, common.Hash{}, nil, false, nil, nil) require.NoError(t, err) require.NotNil(t, block) if requireTx != -1 { diff --git a/miner/miner.go b/miner/miner.go index a01aa5d88f..623c2673c9 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -308,7 +308,19 @@ func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscript // Accepts the block, time at which orders were taken, bundles which were used to build the block and all bundles that were considered for the block type BlockHookFn = func(*types.Block, *big.Int, time.Time, []types.SimulatedBundle, []types.SimulatedBundle, []types.UsedSBundle) +type TobBlockHookFn = func(*types.Block, *big.Int) + +// BuildPayload builds the payload according to the provided parameters. +func (miner *Miner) PayloadAssembler(args *BuildPayloadArgs) (*Payload, error) { + return miner.worker.payloadAssembler(args) +} + // BuildPayload builds the payload according to the provided parameters. func (miner *Miner) BuildPayload(args *BuildPayloadArgs) (*Payload, error) { return miner.worker.buildPayload(args) } + +// BuildPayload builds the payload according to the provided parameters. +func (miner *Miner) BuildTobPayload(args *BuildPayloadArgs) (*Payload, error) { + return miner.worker.buildTobPayload(args) +} diff --git a/miner/multi_worker.go b/miner/multi_worker.go index 830c5d9361..6940bb2b28 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -2,6 +2,7 @@ package miner import ( "errors" + "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -86,18 +87,115 @@ func (w *multiWorker) disablePreseal() { } } -func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { - if args.ProposerCommitment != 1 { - log.Error("TOB_ROB_SPLIT not supported yet") - return nil, errors.New("TOB_ROB_SPLIT not supported yet") +func (w *multiWorker) buildTobPayload(args *BuildPayloadArgs) (*Payload, error) { + if args.ProposerCommitment != 2 { + log.Error("TOB payload building only built for TOB_ROB_SPLIT proposer_commitment") + return nil, errors.New("TOB payload building only built for TOB_ROB_SPLIT proposer_commitment") + } + log.Info("DEBUG: Starting to build TOB payload!!\n") + // Build the initial version with no transaction included. It should be fast + // enough to run. The empty payload can at least make sure there is something + // to deliver for not missing slot. + var empty *types.Block + for _, worker := range w.workers { + var err error + empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) + if err != nil { + log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) + continue + } + break + } + + if empty == nil { + return nil, errors.New("no worker could build an empty block") + } + + log.Info("DEBUG: Got empty TOB payload!!\n") + + // Construct a payload object for return. + payload := newPayload(empty, args.Id()) + + if len(w.workers) == 0 { + return payload, nil + } + + var tobBlock *types.Block + var fees *big.Int + for _, worker := range w.workers { + var err error + start := time.Now() + tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, true, args.BlockHook, args.TobBlockHook, nil) + if err != nil { + log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) + continue + } + log.Info("DEBUG: Got TOB payload!!\n") + log.Info("DEBUG: TOB Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") + payload.update(tobBlock, fees, time.Since(start)) + break + } + + return payload, nil +} + +func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) { + log.Info("DEBUG: Starting to assemble payload!!!!\n") + // Build the initial version with no transaction included. It should be fast + // enough to run. The empty payload can at least make sure there is something + // to deliver for not missing slot. + var empty *types.Block + for _, worker := range w.workers { + var err error + empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) + if err != nil { + log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) + continue + } + break + } + + if empty == nil { + return nil, errors.New("no worker could build an empty block") + } + + log.Info("DEBUG: Got empty assembler payload!!\n") + + // Construct a payload object for return. + payload := newPayload(empty, args.Id()) + + if len(w.workers) == 0 { + return payload, nil } + + var tobBlock *types.Block + var fees *big.Int + for _, worker := range w.workers { + var err error + start := time.Now() + tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, true, args.BlockHook, args.TobBlockHook, args.AssemblerTxs) + if err != nil { + log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) + continue + } + log.Info("DEBUG: Got assembler payload!!\n") + log.Info("DEBUG: assembler Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") + payload.update(tobBlock, fees, time.Since(start)) + break + } + + return payload, nil +} + +func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { + log.Info("DEBUG: In ROB payload building!!\n") // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. var empty *types.Block for _, worker := range w.workers { var err error - empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, nil) + empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) if err != nil { log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) continue @@ -126,7 +224,7 @@ func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { go func(w *worker) { // Update routine done elsewhere! start := time.Now() - block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook) + block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, false, args.BlockHook, args.TobBlockHook, nil) if err == nil { workerPayload.update(block, fees, time.Since(start)) } else { diff --git a/miner/payload_building.go b/miner/payload_building.go index 332425ff86..cc7246518d 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -42,7 +42,9 @@ type BuildPayloadArgs struct { Random common.Hash // The provided randomness value Withdrawals types.Withdrawals // The provided withdrawals BlockHook BlockHookFn - ProposerCommitment uint64 // The provided proposer commitment + TobBlockHook TobBlockHookFn + ProposerCommitment uint64 // The provided proposer commitment + AssemblerTxs map[common.Address]types.Transactions // The txs to be assembled } // Id computes an 8-byte identifier by hashing the components of the payload arguments. @@ -222,7 +224,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. - empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, args.BlockHook) + empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, args.BlockHook, args.TobBlockHook, nil) if err != nil { return nil, err } @@ -246,7 +248,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { select { case <-timer.C: start := time.Now() - block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook) + block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, false, args.BlockHook, args.TobBlockHook, nil) if err == nil { payload.update(block, fees, time.Since(start)) } diff --git a/miner/verify_bundles.go b/miner/verify_bundles.go index 300e109b0b..3cded68256 100644 --- a/miner/verify_bundles.go +++ b/miner/verify_bundles.go @@ -113,6 +113,10 @@ func (e *ErrUnexpectedTx) Error() string { // 3. All txs from the bundle must be in the right order, gaps between txs are allowed // 4. All txs in the block are either from mempool or from the included bundles func VerifyBundlesAtomicity(env *environment, committedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, mempoolTxHashes map[common.Hash]struct{}) error { + // TOB blocks do not contain bundles + if env.isTob { + return nil + } // bundleHash -> tx includedBundles := make(bundleHashToTransactionDataMap). ExtractFromBundles(committedBundles). diff --git a/miner/worker.go b/miner/worker.go index d9fb0e465b..64bd120346 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -95,18 +95,21 @@ var ( type environment struct { signer types.Signer - state *state.StateDB // apply state changes here - ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) - family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) - tcount int // tx count in cycle - gasPool *core.GasPool // available gas used to pack transactions - coinbase common.Address - profit *big.Int - - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt - uncles map[common.Hash]*types.Header + state *state.StateDB // apply state changes here + ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) + family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) + tcount int // tx count in cycle + gasPool *core.GasPool // available gas used to pack transactions + coinbase common.Address + profit *big.Int + isTob bool + isAssembler bool + + header *types.Header + txs []*types.Transaction + assemblerTxs map[common.Address]types.Transactions + receipts []*types.Receipt + uncles map[common.Hash]*types.Header } // copy creates a deep copy of environment. @@ -121,6 +124,7 @@ func (env *environment) copy() *environment { profit: new(big.Int).Set(env.profit), header: types.CopyHeader(env.header), receipts: copyReceipts(env.receipts), + isTob: env.isTob, } if env.gasPool != nil { gasPool := *env.gasPool @@ -883,7 +887,7 @@ func (w *worker) resultLoop() { } // makeEnv creates a new environment for the sealing block. -func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { +func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address, isTob bool, assemblerTxs map[common.Address]types.Transactions) (*environment, error) { // Retrieve the parent state to execute on top and start a prefetcher for // the miner to speed block sealing up a bit. state, err := w.chain.StateAt(parent.Root) @@ -902,6 +906,11 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co header: header, uncles: make(map[common.Hash]*types.Header), profit: new(big.Int), + isTob: isTob, + } + if assemblerTxs != nil && len(assemblerTxs) > 0 { + env.isAssembler = true + env.assemblerTxs = assemblerTxs } // when 08 is processed ancestors contain 07 (quick block) for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { @@ -987,16 +996,29 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]* } } + if env.isTob { + log.Info("DEBUG: Applying TOB tx with nonce!\n", "tx", tx.Nonce()) + log.Info("DEBUG: Applying TOB tx!\n") + } receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, &gasPool, stateDB, env.header, tx, &envGasUsed, config, hook) if err != nil { + if env.isTob { + log.Info("DEBUG: ApplyTransaction failed with!\n", "err", err) + } stateDB.RevertToSnapshot(snapshot) return nil, err } + if env.isTob { + log.Info("DEBUG: ApplyTransaction succeeded!\n") + } *env.gasPool = gasPool env.header.GasUsed = envGasUsed env.state = stateDB + if env.isTob { + log.Info("DEBUG: Adding tx to env!\n") + } env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) @@ -1095,7 +1117,121 @@ func (w *worker) commitBundle(env *environment, txs types.Transactions, interrup return nil } +// TOB txs are picked out from the mempool by certain heuristics. It is highly likely that these txs will be nonce gapped +// due to this the tx commitment will never succeed. We also cannot pick txs in such a way that we avoid nonce gap as this will reduce +// the search space of txs which can be included in the TOB. +// To bypass this, we don't commit the TOB txs but simply send them to the relayer. The actual commitment of the TOB txs will +// happen in the relayer payload assembler which will assemble the TOB and ROB block +func (w *worker) mockCommitTobTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) error { + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs") + } + gasLimit := env.header.GasLimit + if env.gasPool == nil { + env.gasPool = new(core.GasPool).AddGas(gasLimit) + } + var coalescedLogs []*types.Log + + for { + // Check interruption signal and abort building if it's fired. + if interrupt != nil { + if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { + return signalToErr(signal) + } + } + // If we don't have enough gas for any further transactions then we're done. + if env.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + break + } + // Retrieve the next transaction and abort if all done + order := txs.Peek() + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs", "order", order) + } + if order == nil { + break + } + tx := order.Tx() + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs", "tx", tx) + } + if tx == nil { + continue + } + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + from, _ := types.Sender(env.signer, tx) + + // Check whether the tx is replay protected. If we're not in the EIP155 hf + // phase, start ignoring the sender until we do. + if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + + txs.Pop() + continue + } + + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs, we are commiting txs now") + } + logs, err := w.commitTransaction(env, tx) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + txs.Pop() + + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + txs.Shift() + + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + txs.Pop() + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + txs.Shift() + + case errors.Is(err, types.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + txs.Pop() + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + txs.Shift() + } + } + if !w.isRunning() && len(coalescedLogs) > 0 { + // We don't push the pendingLogsEvent while we are sealing. The reason is that + // when we are sealing, the worker will regenerate a sealing block every 3 seconds. + // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. + + // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined + // logs by filling in the block hash when the block was mined by the local miner. This can + // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. + cpy := make([]*types.Log, len(coalescedLogs)) + for i, l := range coalescedLogs { + cpy[i] = new(types.Log) + *cpy[i] = *l + } + w.pendingLogsFeed.Send(cpy) + } + return nil +} + func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) error { + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs") + } gasLimit := env.header.GasLimit if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) @@ -1116,10 +1252,16 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP } // Retrieve the next transaction and abort if all done order := txs.Peek() + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs", "order", order) + } if order == nil { break } tx := order.Tx() + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs", "tx", tx) + } if tx == nil { continue } @@ -1136,6 +1278,9 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP continue } + if env.isTob { + log.Info("DEBUG: In Commiting TOB txs, we are commiting txs now") + } logs, err := w.commitTransaction(env, tx) switch { case errors.Is(err, core.ErrGasLimitReached): @@ -1191,16 +1336,20 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP // generateParams wraps various of settings for generating sealing task. type generateParams struct { - timestamp uint64 // The timstamp for sealing task - forceTime bool // Flag whether the given timestamp is immutable or not - parentHash common.Hash // Parent block hash, empty means the latest chain head - coinbase common.Address // The fee recipient address for including transaction - gasLimit uint64 // The validator's requested gas limit target - random common.Hash // The randomness generated by beacon chain, empty before the merge - withdrawals types.Withdrawals // List of withdrawals to include in block. - noUncle bool // Flag whether the uncle block inclusion is allowed - noTxs bool // Flag whether an empty block without any transaction is expected - onBlock BlockHookFn // Callback to call for each produced block + timestamp uint64 // The timstamp for sealing task + forceTime bool // Flag whether the given timestamp is immutable or not + parentHash common.Hash // Parent block hash, empty means the latest chain head + coinbase common.Address // The fee recipient address for including transaction + gasLimit uint64 // The validator's requested gas limit target + random common.Hash // The randomness generated by beacon chain, empty before the merge + withdrawals types.Withdrawals // List of withdrawals to include in block. + noUncle bool // Flag whether the uncle block inclusion is allowed + noTxs bool // Flag whether an empty block without any transaction is expected + onBlock BlockHookFn // Callback to call for each produced block + onTobBlock TobBlockHookFn // Callback to call for each produced tob block + isTobBlock bool // Are we generating a tob block or a normal/rob block + isAssembler bool // Are we generating a block as an assembler + assemblerTxs map[common.Address]types.Transactions // The transactions that the assembler wants to make a block out of } func doPrepareHeader(genParams *generateParams, chain *core.BlockChain, config *Config, chainConfig *params.ChainConfig, extra []byte, engine consensus.Engine) (*types.Header, *types.Header, error) { @@ -1277,7 +1426,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { // Could potentially happen if starting to mine in an odd state. // Note genParams.coinbase can be different with header.Coinbase // since clique algorithm can modify the coinbase field in header. - env, err := w.makeEnv(parent, header, genParams.coinbase) + env, err := w.makeEnv(parent, header, genParams.coinbase, genParams.isTobBlock, genParams.assemblerTxs) if err != nil { log.Error("Failed to create sealing context", "err", err) return nil, err @@ -1311,6 +1460,17 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) mempoolTxHashes map[common.Hash]struct{} err error ) + if env.isTob { + log.Info("DEBUG: Filling up TOB txs!!\n") + blockBundles, allBundles, mempoolTxHashes, err = w.fillTobTransactions(interrupt, env) + return blockBundles, allBundles, usedSbundles, mempoolTxHashes, err + } + if env.isAssembler { + log.Info("DEBUG: Actually assembling the txs!!\n") + blockBundles, allBundles, mempoolTxHashes, err = w.fillAssemblerTransactions(interrupt, env) + return blockBundles, allBundles, usedSbundles, mempoolTxHashes, err + } + log.Info("DEBUG: Filling up ROB txs!!!\n") switch w.flashbots.algoType { case ALGO_GREEDY, ALGO_GREEDY_BUCKETS: blockBundles, allBundles, usedSbundles, mempoolTxHashes, err = w.fillTransactionsAlgoWorker(interrupt, env) @@ -1322,6 +1482,116 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) return blockBundles, allBundles, usedSbundles, mempoolTxHashes, err } +// fillTransactions retrieves the pending transactions from the txpool and fills them +// into the given sealing block. The transaction selection and ordering strategy can +// be customized with the plugin in the future. +// Returns error if any, otherwise the bundles that made it into the block and all bundles that passed simulation +func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ([]types.SimulatedBundle, []types.SimulatedBundle, map[common.Hash]struct{}, error) { + log.Info("DEBUG: In fillAssemblerTransactions!!!\n") + + assemblerTxs := env.assemblerTxs + + if len(assemblerTxs) > 0 { + log.Info("DEBUG: Commiting assembler txs!!!\n") + txs := types.NewTransactionsByPriceAndNonce(env.signer, assemblerTxs, nil, nil, env.header.BaseFee) + if err := w.commitTransactions(env, txs, interrupt); err != nil { + return nil, nil, nil, err + } + } + + log.Info("DEBUG: Assembler environment is ", "env", env) + + return []types.SimulatedBundle{}, []types.SimulatedBundle{}, map[common.Hash]struct{}{}, nil +} + +// fillTransactions retrieves the pending transactions from the txpool and fills them +// into the given sealing block. The transaction selection and ordering strategy can +// be customized with the plugin in the future. +// Returns error if any, otherwise the bundles that made it into the block and all bundles that passed simulation +func (w *worker) fillTobTransactions(interrupt *int32, env *environment) ([]types.SimulatedBundle, []types.SimulatedBundle, map[common.Hash]struct{}, error) { + log.Info("DEBUG: In fillTobTransactions!!!\n") + // Split the pending transactions into locals and remotes + // Fill the block with all available pending transactions. + pending := w.eth.TxPool().Pending(true) + mempoolTxHashes := make(map[common.Hash]struct{}, len(pending)) + // filter the txs for uniswap v2 router tx with the highest value + tobPending := make(map[common.Address]types.Transactions) + + log.Info("DEBUG: pending txs are \n", "pending", pending) + + type highestValueTx struct { + tx *types.Transaction + value *big.Int + from common.Address + } + + // store the highest value tx here + var highestValueTxTracker = highestValueTx{ + tx: nil, + value: big.NewInt(0), + from: common.Address{}, + } + // store the highest value tx from uniswap here + var highestValueUniswapTxTracker = highestValueTx{ + tx: nil, + value: big.NewInt(0), + from: common.Address{}, + } + for account, txs := range pending { + for _, tx := range txs { + if tx.Value().Cmp(highestValueTxTracker.value) == 1 { + log.Info("DEBUG: tx.Value() is \n", "tx.Value()", tx.Value()) + log.Info("DEBUG: tx.To() is \n", "tx.To()", *tx.To()) + log.Info("DEBUG: tx.From() is \n", "tx.From()", account) + log.Info("DEBUG: tx.Nonce() is \n", "tx.Nonce()", tx.Nonce()) + highestValueTxTracker.tx = tx + highestValueTxTracker.value = tx.Value() + highestValueTxTracker.from = account + } + if tx.To() != nil { + log.Info("DEBUG: tx.To() is \n", "tx.To()", *tx.To()) + log.Info("DEBUG: tx.Value() is \n", "tx.Value()", tx.Value()) + log.Info("DEBUG: tx.From() is \n", "tx.From()", account) + log.Info("DEBUG: tx.Nonce() is \n", "tx.Nonce()", tx.Nonce()) + if *tx.To() == common.HexToAddress("0xB9D7a3554F221B34f49d7d3C61375E603aFb699e") { + log.Info("DEBUG: Got a uniswap v2 tx!!!\n") + if tx.Value().Cmp(highestValueUniswapTxTracker.value) >= 0 { + highestValueUniswapTxTracker.tx = tx + highestValueUniswapTxTracker.value = tx.Value() + highestValueUniswapTxTracker.from = account + } + } + } + } + } + log.Info("DEBUG: highestValueTxTracker is \n", "highestValueTxTracker", highestValueTxTracker) + log.Info("DEBUG: highestValueUniswapTxTracker is \n", "highestValueUniswapTxTracker", highestValueUniswapTxTracker) + + if highestValueUniswapTxTracker.tx != nil { + env.txs = append(env.txs, highestValueUniswapTxTracker.tx) + env.tcount++ + //tobPending[highestValueUniswapTxTracker.from] = append(tobPending[highestValueUniswapTxTracker.from], highestValueUniswapTxTracker.tx) + } else if highestValueTxTracker.tx != nil { + env.txs = append(env.txs, highestValueTxTracker.tx) + env.tcount++ + //tobPending[highestValueTxTracker.from] = append(tobPending[highestValueTxTracker.from], highestValueTxTracker.tx) + } + log.Info("DEBUG: env post TOB tx checking is!\n", "env", env) + //log.Info("DEBUG: tobPending is \n", "tobPending", tobPending) + + if len(tobPending) > 0 { + log.Info("DEBUG: Commiting TOB txs!!!\n") + //txs := types.NewTransactionsByPriceAndNonce(env.signer, tobPending, nil, nil, env.header.BaseFee) + //if err := w.commitTransactions(env, txs, interrupt); err != nil { + // return nil, nil, nil, err + //} + } + + log.Info("DEBUG: TOB environment is ", "env", env) + + return []types.SimulatedBundle{}, []types.SimulatedBundle{}, mempoolTxHashes, nil +} + // fillTransactions retrieves the pending transactions from the txpool and fills them // into the given sealing block. The transaction selection and ordering strategy can // be customized with the plugin in the future. @@ -1346,6 +1616,8 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) ([]types.S var blockBundles []types.SimulatedBundle var allBundles []types.SimulatedBundle + // TOB block will not hold any bundles + if w.flashbots.isFlashbots { bundles, ccBundleCh := w.eth.TxPool().MevBundles(env.header.Number, env.header.Time) bundles = append(bundles, <-ccBundleCh...) @@ -1503,7 +1775,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e finalizeFn := func(env *environment, orderCloseTime time.Time, blockBundles []types.SimulatedBundle, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, noTxs bool) (*types.Block, *big.Int, error) { - block, profit, err := w.finalizeBlock(env, params.withdrawals, validatorCoinbase, noTxs) + block, profit, err := w.finalizeBlock(env, params.withdrawals, validatorCoinbase, noTxs, params.isTobBlock) if err != nil { log.Error("could not finalize block", "err", err) return nil, nil, err @@ -1528,8 +1800,14 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e gasUsedGauge.Update(int64(block.GasUsed())) transactionNumGauge.Update(int64(len(env.txs))) } - if params.onBlock != nil { - go params.onBlock(block, profit, orderCloseTime, blockBundles, allBundles, usedSbundles) + if !params.isAssembler { + if params.isTobBlock && params.onTobBlock != nil { + log.Info("DEBUG: Running TOB block hook!!\n") + go params.onTobBlock(block, profit) + } else if params.onBlock != nil { + log.Info("DEBUG: Running ROB block hook!!\n") + go params.onBlock(block, profit, orderCloseTime, blockBundles, allBundles, usedSbundles) + } } return block, profit, nil @@ -1539,13 +1817,18 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, time.Now(), nil, nil, nil, true) } - paymentTxReserve, err := w.proposerTxPrepare(work, &validatorCoinbase) - if err != nil { - return nil, nil, err - } + // TODO - Add tob validator payment support + //var paymentTxReserve *proposerTxReservation + //if !params.isTobBlock || !params.isAssembler { + // paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) + // if err != nil { + // return nil, nil, err + // } + //} orderCloseTime := time.Now() + log.Info("DEBUG: env isTob", "isTob", work.isTob) blockBundles, allBundles, usedSbundles, mempoolTxHashes, err := w.fillTransactionsSelectAlgo(nil, work) if err != nil { return nil, nil, err @@ -1574,15 +1857,17 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, true) } - err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) - if err != nil { - return nil, nil, err - } + //if !params.isTobBlock || !params.isAssembler { + // err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) + // if err != nil { + // return nil, nil, err + // } + //} return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, false) } -func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, validatorCoinbase common.Address, noTxs bool) (*types.Block, *big.Int, error) { +func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, validatorCoinbase common.Address, noTxs bool, isTobBlock bool) (*types.Block, *big.Int, error) { block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, withdrawals) if err != nil { return nil, nil, err @@ -1596,10 +1881,14 @@ func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, return block, big.NewInt(0), nil } - blockProfit, err := w.checkProposerPayment(work, validatorCoinbase) - if err != nil { - return nil, nil, err - } + var blockProfit = big.NewInt(0) + // TODO - support proposer payments for TOB + //if !isTobBlock { + // blockProfit, err = w.checkProposerPayment(work, validatorCoinbase) + // if err != nil { + // return nil, nil, err + // } + //} return block, blockProfit, nil } @@ -1731,19 +2020,22 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti // getSealingBlock generates the sealing block based on the given parameters. // The generation result will be passed back via the given channel no matter // the generation itself succeeds or not. -func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, gasLimit uint64, random common.Hash, withdrawals types.Withdrawals, noTxs bool, blockHook BlockHookFn) (*types.Block, *big.Int, error) { +func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, gasLimit uint64, random common.Hash, withdrawals types.Withdrawals, noTxs bool, isTobBlock bool, blockHook BlockHookFn, tobBlockHook TobBlockHookFn, assemblerTxs map[common.Address]types.Transactions) (*types.Block, *big.Int, error) { req := &getWorkReq{ params: &generateParams{ - timestamp: timestamp, - forceTime: true, - parentHash: parent, - coinbase: coinbase, - gasLimit: gasLimit, - random: random, - withdrawals: withdrawals, - noUncle: true, - noTxs: noTxs, - onBlock: blockHook, + timestamp: timestamp, + forceTime: true, + parentHash: parent, + coinbase: coinbase, + gasLimit: gasLimit, + random: random, + withdrawals: withdrawals, + noUncle: true, + noTxs: noTxs, + onBlock: blockHook, + onTobBlock: tobBlockHook, + isTobBlock: isTobBlock, + assemblerTxs: assemblerTxs, }, result: make(chan *newPayloadResult, 1), } diff --git a/miner/worker_test.go b/miner/worker_test.go index bc57593f38..06da3db3e5 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -658,7 +658,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is not enabled for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, true, nil) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, true, false, nil, nil, nil) if c.expectErr { if err == nil { t.Error("Expect error but get nil") @@ -674,7 +674,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is enabled w.start() for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, false, nil) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, false, false, nil, nil, nil) if c.expectErr { if err == nil { t.Error("Expect error but get nil") @@ -822,7 +822,7 @@ func testBundles(t *testing.T) { require.NoError(t, err) } - block, _, err := w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, nil) + block, _, err := w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, false, nil, nil, nil) require.NoError(t, err) state, err := w.chain.State() From 88c4b7eedfb4d4d57b07fb40181fac2e5e3ab765 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Fri, 8 Sep 2023 22:17:52 +0530 Subject: [PATCH 03/19] pepc-boost impl 2 --- beacon/engine/types.go | 8 +- builder/builder.go | 96 ++------ builder/eth_service.go | 43 +--- builder/eth_service_test.go | 2 - builder/resubmit_utils.go | 3 +- core/types/builder.go | 4 +- core/types/transaction.go | 9 + core/types/withdrawal.go | 4 + eth/block-validation/api.go | 186 +++++++++------- eth/block-validation/api_test.go | 30 +++ miner/contract_simulator_test.go | 2 +- miner/miner.go | 5 - miner/multi_worker.go | 60 +---- miner/payload_building.go | 27 ++- miner/verify_bundles.go | 4 - miner/worker.go | 361 +++++++++++-------------------- miner/worker_test.go | 123 ++++++++++- 17 files changed, 457 insertions(+), 510 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 37a7dc73ac..00bcf26ad3 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -141,7 +141,7 @@ func encodeTransactions(txs []*types.Transaction) [][]byte { return enc } -func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { +func DecodeTransactions(enc [][]byte) ([]*types.Transaction, error) { var txs = make([]*types.Transaction, len(enc)) for i, encTx := range enc { var tx types.Transaction @@ -164,7 +164,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // Withdrawals value will propagate through the returned block. Empty // Withdrawals value must be passed via non-nil, length 0 value in params. func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) { - txs, err := decodeTransactions(params.Transactions) + txs, err := DecodeTransactions(params.Transactions) if err != nil { return nil, err } @@ -246,7 +246,7 @@ func ExecutionPayloadToBlock(payload *bellatrix.ExecutionPayload) (*types.Block, for i, txHexBytes := range payload.Transactions { transactionBytes[i] = txHexBytes[:] } - txs, err := decodeTransactions(transactionBytes) + txs, err := DecodeTransactions(transactionBytes) if err != nil { return nil, err } @@ -286,7 +286,7 @@ func ExecutionPayloadV2ToBlock(payload *capella.ExecutionPayload) (*types.Block, for i, txHexBytes := range payload.Transactions { transactionBytes[i] = txHexBytes[:] } - txs, err := decodeTransactions(transactionBytes) + txs, err := DecodeTransactions(transactionBytes) if err != nil { return nil, err } diff --git a/builder/builder.go b/builder/builder.go index 2e211ffd34..c9cc6471d7 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -48,10 +48,8 @@ type ValidatorData struct { type IRelay interface { SubmitBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error - SubmitTobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error - SubmitTobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) Config() RelayConfig @@ -207,10 +205,10 @@ func (b *Builder) Stop() error { func (b *Builder) onSealedBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isTobBlock bool, isRobBlock bool) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isRobBlock bool) error { if b.eth.Config().IsShanghai(block.Time()) { log.Info("DEBUG: Submitting capella block\n") - if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs, isTobBlock, isRobBlock); err != nil { + if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs, isRobBlock); err != nil { return err } } else { @@ -286,7 +284,7 @@ func (b *Builder) submitBellatrixBlock(block *types.Block, blockValue *big.Int, func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isTobBlock bool, isRobBlock bool) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isRobBlock bool) error { executableData := engine.BlockToExecutableData(block, blockValue) payload, err := executableDataToCapellaExecutionPayload(executableData.ExecutionPayload) if err != nil { @@ -331,14 +329,7 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or } } else { go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg) - if isTobBlock { - log.Info("DEBUG: submitting tob capella block") - err = b.relay.SubmitTobBlockCapella(&blockSubmitReq, vd) - if err != nil { - log.Error("could not submit tob capella block", "err", err, "#commitedBundles", len(commitedBundles)) - return err - } - } else if isRobBlock { + if isRobBlock { log.Info("DEBUG: submitting rob capella block") err = b.relay.SubmitRobBlockCapella(&blockSubmitReq, vd) if err != nil { @@ -346,6 +337,7 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or return err } } else { + log.Info("DEBUG: Submitting regular FULL_BLOCK capella block") err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) if err != nil { log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) @@ -371,7 +363,6 @@ func (b *Builder) OnPayloadAttribute(attrs *types.BuilderPayloadAttributes) erro log.Info("DEBUG: validator info is", "vd", vd) attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) attrs.GasLimit = vd.GasLimit - attrs.ProposerCommitment = vd.ProposerCommitment proposerPubkey, err := utils.HexToPubkey(string(vd.Pubkey)) if err != nil { @@ -432,27 +423,20 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. var ( queueSignal = make(chan struct{}, 1) - tobQueueMu sync.Mutex - queueMu sync.Mutex - queueLastSubmittedTobHash common.Hash - queueLastSubmittedHash common.Hash - queueBestTobEntry blockQueueEntry - queueBestEntry blockQueueEntry + queueMu sync.Mutex + queueLastSubmittedHash common.Hash + queueBestEntry blockQueueEntry ) - log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp), "proposerCommitment", attrs.ProposerCommitment, "gasLimit", attrs.GasLimit) + log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp), "gasLimit", attrs.GasLimit) submitBestBlock := func() { log.Info("DEBUG: In submit best block!") - isRobBlock := false - if attrs.ProposerCommitment == 2 { - isRobBlock = true - } - log.Info("DEBUG: Submitting ROB block!!", "isRobBlock", isRobBlock) + log.Info("DEBUG: Submitting ROB block!!", "isRobBlock", true) queueMu.Lock() if queueBestEntry.block != nil && queueBestEntry.block.Hash() != queueLastSubmittedHash { err := b.onSealedBlock(queueBestEntry.block, queueBestEntry.blockValue, queueBestEntry.ordersCloseTime, queueBestEntry.sealedAt, - queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs, false, isRobBlock) + queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs, true) if err != nil { log.Error("could not run sealed block hook", "err", err) @@ -463,30 +447,13 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. queueMu.Unlock() } - submitBestTobBlock := func() { - log.Info("DEBUG: In submitBestTobBlock\n") - tobQueueMu.Lock() - if queueBestTobEntry.block != nil && queueBestTobEntry.block.Hash() != queueLastSubmittedTobHash { - log.Info("DEBUG: Running onSealedBlock in submitBestTobBlock!!\n") - err := b.onSealedBlock(queueBestTobEntry.block, queueBestTobEntry.blockValue, queueBestTobEntry.ordersCloseTime, queueBestTobEntry.sealedAt, - queueBestTobEntry.commitedBundles, queueBestTobEntry.allBundles, queueBestTobEntry.usedSbundles, proposerPubkey, vd, attrs, true, false) - - if err != nil { - log.Error("could not run sealed tob block hook", "err", err) - } else { - queueLastSubmittedTobHash = queueBestTobEntry.block.Hash() - } - } - tobQueueMu.Unlock() - } - // Avoid submitting early into a given slot. For example if slots have 12 second interval, submissions should // not begin until 8 seconds into the slot. slotTime := time.Unix(int64(attrs.Timestamp), 0).UTC() slotSubmitStartTime := slotTime.Add(-b.submissionOffsetFromEndOfSlot) // Empties queue, submits the best block for current job with rate limit (global for all jobs) - go runResubmitLoop(ctx, b.limiter, queueSignal, submitBestBlock, submitBestTobBlock, slotSubmitStartTime) + go runResubmitLoop(ctx, b.limiter, queueSignal, submitBestBlock, slotSubmitStartTime) // Populates queue with submissions that increase block profit blockHook := func(block *types.Block, blockValue *big.Int, ordersCloseTime time.Time, @@ -519,42 +486,13 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. } } - // Populates queue with submissions that increase block profit - tobBlockHook := func(block *types.Block, blockValue *big.Int) { - if ctx.Err() != nil { - return - } - - log.Info("DEBUG: Running TOB Block hook!!!!\n") - sealedAt := time.Now() - - tobQueueMu.Lock() - defer tobQueueMu.Unlock() - if block.Hash() != queueLastSubmittedTobHash { - queueBestTobEntry = blockQueueEntry{ - block: block, - blockValue: new(big.Int).Set(blockValue), - ordersCloseTime: time.Time{}, - sealedAt: sealedAt, - commitedBundles: []types.SimulatedBundle{}, - allBundles: []types.SimulatedBundle{}, - usedSbundles: []types.UsedSBundle{}, - } - - select { - case queueSignal <- struct{}{}: - default: - } - } - } - // resubmits block builder requests every builderBlockResubmitInterval runRetryLoop(ctx, b.builderResubmitInterval, func() { log.Debug("retrying BuildBlock", "slot", attrs.Slot, "parent", attrs.HeadHash, "resubmit-interval", b.builderResubmitInterval.String()) - err := b.eth.BuildBlock(attrs, blockHook, tobBlockHook) + err := b.eth.BuildBlock(attrs, blockHook) if err != nil { log.Warn("Failed to build block", "err", err) } @@ -594,7 +532,7 @@ func executableDataToExecutionPayload(data *engine.ExecutableData) (*bellatrix.E func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*capella.ExecutionPayload, error) { transactionData := make([]bellatrix.Transaction, len(data.Transactions)) for i, tx := range data.Transactions { - transactionData[i] = bellatrix.Transaction(tx) + transactionData[i] = tx } withdrawalData := make([]*capella.Withdrawal, len(data.Withdrawals)) @@ -616,10 +554,10 @@ func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*cape return &capella.ExecutionPayload{ ParentHash: [32]byte(data.ParentHash), FeeRecipient: [20]byte(data.FeeRecipient), - StateRoot: [32]byte(data.StateRoot), - ReceiptsRoot: [32]byte(data.ReceiptsRoot), + StateRoot: data.StateRoot, + ReceiptsRoot: data.ReceiptsRoot, LogsBloom: types.BytesToBloom(data.LogsBloom), - PrevRandao: [32]byte(data.Random), + PrevRandao: data.Random, BlockNumber: data.Number, GasLimit: data.GasLimit, GasUsed: data.GasUsed, diff --git a/builder/eth_service.go b/builder/eth_service.go index d2bc79ab7c..777d2fd6de 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -15,7 +15,7 @@ import ( ) type IEthereumService interface { - BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error + BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error GetBlockByHash(hash common.Hash) *types.Block Config() *params.ChainConfig Synced() bool @@ -31,10 +31,7 @@ type testEthereumService struct { testUsedSbundles []types.UsedSBundle } -func (t *testEthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error { - if attrs.ProposerCommitment > 0 { - return errors.New("proposeCommitment is not supported in test ethereum service") - } +func (t *testEthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { sealedBlockCallback(t.testBlock, t.testBlockValue, time.Now(), t.testBundlesMerged, t.testAllBundles, t.testUsedSbundles) return nil } @@ -54,37 +51,21 @@ func NewEthereumService(eth *eth.Ethereum) *EthereumService { } // TODO: we should move to a setup similar to catalyst local blocks & payload ids -func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn, sealedTobBlockCallback miner.TobBlockHookFn) error { +func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { // Send a request to generate a full block in the background. // The result can be obtained via the returned channel. log.Info("DEBUG: In build block!!") args := &miner.BuildPayloadArgs{ - Parent: attrs.HeadHash, - Timestamp: uint64(attrs.Timestamp), - FeeRecipient: attrs.SuggestedFeeRecipient, - GasLimit: attrs.GasLimit, - Random: attrs.Random, - Withdrawals: attrs.Withdrawals, - BlockHook: sealedBlockCallback, - TobBlockHook: sealedTobBlockCallback, - ProposerCommitment: attrs.ProposerCommitment, + Parent: attrs.HeadHash, + Timestamp: uint64(attrs.Timestamp), + FeeRecipient: attrs.SuggestedFeeRecipient, + GasLimit: attrs.GasLimit, + Random: attrs.Random, + Withdrawals: attrs.Withdrawals, + BlockHook: sealedBlockCallback, } log.Info("DEBUG: Args for build block!!", "args", args) - tobResCh := make(chan *engine.ExecutionPayloadEnvelope, 1) - if attrs.ProposerCommitment == 2 { - log.Info("DEBUG: Starting to Build TOB Payload!!!") - tobPayload, err := s.eth.Miner().BuildTobPayload(args) - if err != nil { - log.Error("Failed to build tob payload", "err", err) - return err - } - - go func() { - tobResCh <- tobPayload.ResolveFull() - }() - } - log.Info("DEBUG: Starting to build ROB payload!!") payload, err := s.eth.Miner().BuildPayload(args) if err != nil { @@ -105,10 +86,6 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal if payload == nil { return errors.New("received nil tob payload from sealing work") } - case tobPayload := <-tobResCh: - if tobPayload == nil { - return errors.New("received nil tob payload from sealing work") - } case <-timer.C: payload.Cancel() log.Error("timeout waiting for block", "parent hash", attrs.HeadHash, "slot", attrs.Slot) diff --git a/builder/eth_service_test.go b/builder/eth_service_test.go index 79ff05183a..ff3ed7cc23 100644 --- a/builder/eth_service_test.go +++ b/builder/eth_service_test.go @@ -89,7 +89,6 @@ func TestBuildBlockUnsupportedProposerCommitment(t *testing.T) { SuggestedFeeRecipient: common.Address{0x04, 0x10}, GasLimit: uint64(4800000), Slot: uint64(25), - ProposerCommitment: 1, } service := NewEthereumService(ethservice) @@ -114,7 +113,6 @@ func TestBuildBlock(t *testing.T) { SuggestedFeeRecipient: common.Address{0x04, 0x10}, GasLimit: uint64(4800000), Slot: uint64(25), - ProposerCommitment: 0, } service := NewEthereumService(ethservice) diff --git a/builder/resubmit_utils.go b/builder/resubmit_utils.go index cceb9951aa..4819287e6d 100644 --- a/builder/resubmit_utils.go +++ b/builder/resubmit_utils.go @@ -9,7 +9,7 @@ import ( ) // runResubmitLoop checks for update signal and calls submit respecting provided rate limiter and context -func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <-chan struct{}, submit func(), submitTob func(), submitTime time.Time) { +func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <-chan struct{}, submit func(), submitTime time.Time) { if submitTime.IsZero() { log.Warn("skipping resubmit loop - zero submit time found") return @@ -62,7 +62,6 @@ func runResubmitLoop(ctx context.Context, limiter *rate.Limiter, updateSignal <- delay := res.Delay() if delay == 0 { submit() - submitTob() continue } diff --git a/core/types/builder.go b/core/types/builder.go index 5a595de4a8..d31da82fdb 100644 --- a/core/types/builder.go +++ b/core/types/builder.go @@ -14,7 +14,6 @@ type BuilderPayloadAttributes struct { HeadHash common.Hash `json:"blockHash"` Withdrawals Withdrawals `json:"withdrawals"` GasLimit uint64 - ProposerCommitment uint64 `json:"proposerCommitment"` } func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bool { @@ -23,8 +22,7 @@ func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bo attrs.SuggestedFeeRecipient != other.SuggestedFeeRecipient || attrs.Slot != other.Slot || attrs.HeadHash != other.HeadHash || - attrs.GasLimit != other.GasLimit || - attrs.ProposerCommitment != other.ProposerCommitment { + attrs.GasLimit != other.GasLimit { return false } diff --git a/core/types/transaction.go b/core/types/transaction.go index eda68bbb3b..206041760f 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -599,6 +599,15 @@ func NewSBundleWithMinerFee(sbundle *SimSBundle, _ *big.Int) (*TxWithMinerFee, e }, nil } +// Used only to assemble txs. During tx assembly, we need to have txs in the sequence specified by assembly request +//type AssemblerTxList []*TxWithMinerFee +// +//func (s AssemblerTxList) Len() int { return len(s) } +//func (s AssemblerTxList) Less(i, j int) bool { return i < j } +//func (s AssemblerTxList) Swap(i, j int) { +// s[i], s[j] = s[j], s[i] +//} + // TxByPriceAndTime implements both the sort and the heap interface, making it useful // for all at once sorting as well as individually adding and removing elements. type TxByPriceAndTime []*TxWithMinerFee diff --git a/core/types/withdrawal.go b/core/types/withdrawal.go index d1ad918f98..512af2c274 100644 --- a/core/types/withdrawal.go +++ b/core/types/withdrawal.go @@ -45,6 +45,10 @@ type withdrawalMarshaling struct { // Withdrawals implements DerivableList for withdrawals. type Withdrawals []*Withdrawal +func (s Withdrawals) Append(w Withdrawal) Withdrawals { + return append(s, &w) +} + // Len returns the length of s. func (s Withdrawals) Len() int { return len(s) } diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 2a23da9e46..695c238f49 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -6,10 +6,10 @@ import ( "fmt" "math/big" "os" - "sort" bellatrixapi "github.com/attestantio/go-builder-client/api/bellatrix" capellaapi "github.com/attestantio/go-builder-client/api/capella" + "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/beacon/engine" @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" + boostTypes "github.com/flashbots/go-boost-utils/types" ) type BlacklistedAddresses []common.Address @@ -232,109 +233,148 @@ func (r *BuilderBlockValidationRequestV2) UnmarshalJSON(data []byte) error { } type BlockAssemblerRequest struct { - TobPayload *capella.ExecutionPayload `json:"tob_payload"` - TobPayloadBidValue *big.Int `json:"tob_payload_bid_value,string"` - RobPayload *capella.ExecutionPayload `json:"rob_payload"` - RobPayloadBidValue *big.Int `json:"rob_payload_bid_value,string"` + TobTxs [][]byte `json:"tob_txs"` + RobPayload *capellaapi.SubmitBlockRequest `json:"rob_payload"` + RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` + PayloadAttributes *engine.PayloadAttributes `json:"payload_attributes"` } -func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*engine.ExecutionPayloadEnvelope, error) { - if params.TobPayload == nil { - return nil, errors.New("nil tob payload") - } - if params.RobPayload == nil { - return nil, errors.New("nil rob payload") - } - if params.TobPayload.ParentHash != params.RobPayload.ParentHash { - return nil, errors.New("tob and rob payloads have different parent hashes") +// bchain: copied this here to avoid circular dependency +func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*capella.ExecutionPayload, error) { + transactionData := make([]bellatrix.Transaction, len(data.Transactions)) + for i, tx := range data.Transactions { + transactionData[i] = tx } - tobBlock, err := engine.ExecutionPayloadV2ToBlock(params.TobPayload) - if err != nil { - return nil, err + withdrawalData := make([]*capella.Withdrawal, len(data.Withdrawals)) + for i, wd := range data.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), + } } - robBlock, err := engine.ExecutionPayloadV2ToBlock(params.RobPayload) + + baseFeePerGas := new(boostTypes.U256Str) + err := baseFeePerGas.FromBig(data.BaseFeePerGas) if err != nil { return nil, err } - if params.TobPayload.ParentHash != phase0.Hash32(tobBlock.ParentHash()) { - return nil, fmt.Errorf("incorrect ParentHash %s, expected %s", params.TobPayload.ParentHash.String(), tobBlock.ParentHash().String()) + return &capella.ExecutionPayload{ + ParentHash: [32]byte(data.ParentHash), + FeeRecipient: [20]byte(data.FeeRecipient), + StateRoot: data.StateRoot, + ReceiptsRoot: data.ReceiptsRoot, + LogsBloom: types.BytesToBloom(data.LogsBloom), + PrevRandao: data.Random, + BlockNumber: data.Number, + GasLimit: data.GasLimit, + GasUsed: data.GasUsed, + Timestamp: data.Timestamp, + ExtraData: data.ExtraData, + BaseFeePerGas: *baseFeePerGas, + BlockHash: [32]byte(data.BlockHash), + Transactions: transactionData, + Withdrawals: withdrawalData, + }, nil +} + +func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*capella.ExecutionPayload, error) { + if params.RobPayload == nil { + return nil, errors.New("nil rob payload") } - if params.TobPayload.BlockHash != phase0.Hash32(tobBlock.Hash()) { - return nil, fmt.Errorf("incorrect BlockHash %s, expected %s", params.TobPayload.BlockHash.String(), tobBlock.Hash().String()) + + log.Info("DEBUG: Entered the BlockAssembler!!") + log.Info("BlockAssembler", "tobTxs", len(params.TobTxs), "robPayload", params.RobPayload) + transactionBytes := make([][]byte, len(params.TobTxs)) + for i, txHexBytes := range params.TobTxs { + transactionBytes[i] = txHexBytes[:] } - if params.RobPayload.ParentHash != phase0.Hash32(robBlock.Hash()) { - return nil, fmt.Errorf("incorrect ParentHash %s, expected %s", params.RobPayload.BlockHash.String(), robBlock.Hash().String()) + txs, err := engine.DecodeTransactions(transactionBytes) + if err != nil { + return nil, err } - if params.RobPayload.BlockHash != phase0.Hash32(robBlock.Hash()) { - return nil, fmt.Errorf("incorrect BlockHash %s, expected %s", params.RobPayload.BlockHash.String(), robBlock.Hash().String()) + + robBlock, err := engine.ExecutionPayloadV2ToBlock(params.RobPayload.ExecutionPayload) + if err != nil { + return nil, err } // TODO - check for gas limits - // TODO - we still need to support validator payments - - // txs sorted by nonce per address - txMap := make(map[common.Address]types.Transactions) - seenTxs := make(map[common.Hash]struct{}) - assemblerSigner := types.LatestSigner(api.eth.BlockChain().Config()) - for _, tx := range tobBlock.Transactions() { - from, err := types.Sender(assemblerSigner, tx) - if err != nil { - return nil, err - } - txMap[from] = txMap[from].Append(tx) - // check for duplicate txs - // if we see duplicate tx just ignore it, its already added - if _, ok := seenTxs[tx.Hash()]; ok { - continue - } else { - seenTxs[tx.Hash()] = struct{}{} + // TODO - support for payouts + + // check if there are any duplicate txs + // we can error out if there is a nonce gap + // TODO - don't error out, but drop the duplicate tx in the ROB block + log.Info("DEBUG: Checking for duplicate txs") + seenTxMap := make(map[common.Hash]struct{}) + for _, tx := range txs { + // If we see nonce reuse in TOB then fail + if _, ok := seenTxMap[tx.Hash()]; ok { + return nil, errors.New("duplicate tx") } + seenTxMap[tx.Hash()] = struct{}{} } for _, tx := range robBlock.Transactions() { - from, err := types.Sender(assemblerSigner, tx) - if err != nil { - return nil, err - } - - txMap[from] = txMap[from].Append(tx) - // check for duplicate txs - // if we see duplicate tx just ignore it, its already added - if _, ok := seenTxs[tx.Hash()]; ok { - continue - } else { - seenTxs[tx.Hash()] = struct{}{} + // if we see nonce re-use in TOB vs ROB then drop txs the txs in ROB + if _, ok := seenTxMap[tx.Hash()]; ok { + return nil, errors.New("duplicate tx") } + seenTxMap[tx.Hash()] = struct{}{} } - // sort each address's txs by nonce - for _, txs := range txMap { - sort.Slice(txs, func(i, j int) bool { - return txs.Index(i).Nonce() < txs.Index(j).Nonce() + log.Info("Done Checking for duplicate txs!") + + withdrawals := make(types.Withdrawals, len(params.RobPayload.ExecutionPayload.Withdrawals)) + for _, withdrawal := range params.RobPayload.ExecutionPayload.Withdrawals { + withdrawals = withdrawals.Append(types.Withdrawal{ + Index: uint64(withdrawal.Index), + Validator: uint64(withdrawal.ValidatorIndex), + Address: common.Address(withdrawal.Address), + Amount: uint64(withdrawal.Amount), }) } - log.Info("DEBUG PAYLOAD ASSEMBLER: Txmap is ", "txmap", txMap) + log.Info("DEBUG: Built the final set of withdrawals") + log.Info("DEBUG: Withdrawals are ", "withdrawals", withdrawals) // assemble the txs in map[sender]txs format and pass it in the BuildPayload call + + robTxs := robBlock.Transactions() + log.Info("DEBUG: Rob txs are ", "robTxs", robTxs) + log.Info("DEBUG: Tob txs are ", "tobTxs", txs) + log.Info("DEBUG: Assembling the block!!") block, err := api.eth.Miner().PayloadAssembler(&miner.BuildPayloadArgs{ - Parent: common.Hash(params.TobPayload.ParentHash), - Timestamp: params.TobPayload.Timestamp, - // TODO - this should be relayer fee recipient - FeeRecipient: common.Address(params.TobPayload.FeeRecipient), - GasLimit: params.RobPayload.GasLimit, - Random: params.TobPayload.PrevRandao, - Withdrawals: nil, - BlockHook: nil, - TobBlockHook: nil, - ProposerCommitment: 2, - AssemblerTxs: txMap, + Parent: common.Hash(params.RobPayload.ExecutionPayload.ParentHash), + Timestamp: params.RobPayload.ExecutionPayload.Timestamp, + // TODO - this should be relayer fee recipient. We will implement payouts later + FeeRecipient: common.Address(params.RobPayload.ExecutionPayload.FeeRecipient), + GasLimit: params.RegisteredGasLimit, + Random: params.PayloadAttributes.Random, + Withdrawals: withdrawals, + BlockHook: nil, + AssemblerTxs: miner.AssemblerTxLists{ + TobTxs: txs, + RobTxs: &robTxs, + }, }) + log.Info("DEBUG: Assembled the block!!") if err != nil { return nil, err } resolvedBlock := block.ResolveFull() + if resolvedBlock == nil { + return nil, errors.New("unable to resolve block") + } + if resolvedBlock.ExecutionPayload == nil { + return nil, errors.New("nil execution payload") + } + + finalPayload, err := executableDataToCapellaExecutionPayload(resolvedBlock.ExecutionPayload) + log.Info("DEBUG: Final payload", "finalPayload", finalPayload) - return resolvedBlock, nil + log.Info("DEBUG: Returning the final payload!!") + return finalPayload, nil } func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockValidationRequestV2) error { diff --git a/eth/block-validation/api_test.go b/eth/block-validation/api_test.go index dc8f737704..1dd0f59176 100644 --- a/eth/block-validation/api_test.go +++ b/eth/block-validation/api_test.go @@ -170,6 +170,36 @@ func TestValidateBuilderSubmissionV1(t *testing.T) { require.ErrorContains(t, api.ValidateBuilderSubmissionV1(blockRequest), "could not apply tx 4", "insufficient funds for gas * price + value") } +// +//func TestBlockAssembler(t *testing.T) { +// genesis, preMergeBlocks := generatePreMergeChain(20) +// os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) +// time := preMergeBlocks[len(preMergeBlocks)-1].Time() + 5 +// genesis.Config.ShanghaiTime = &time +// n, ethservice := startEthService(t, genesis, preMergeBlocks) +// ethservice.Merger().ReachTTD() +// defer n.Close() +// +// api := NewBlockValidationAPI(ethservice, nil, false) +// parent := preMergeBlocks[len(preMergeBlocks)-1] +// +// api.eth.APIBackend.Miner().SetEtherbase(testBuilderAddr) +// +// statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) +// nonce := statedb.GetNonce(testAddr) +// +// signer := types.LatestSigner(ethservice.BlockChain().Config()) +// +// tobTx1, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x16}, big.NewInt(10), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) +// tobTx2, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x17}, big.NewInt(20), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) +// +// cc, _ := types.SignTx(types.NewContractCreation(nonce+1, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), signer, testKey) +// +// baseFee := misc.CalcBaseFee(params.AllEthashProtocolChanges, preMergeBlocks[len(preMergeBlocks)-1].Header()) +// tx2, _ := types.SignTx(types.NewTransaction(nonce+2, testAddr, big.NewInt(10), 21000, baseFee, nil), signer, testKey) +// +//} + func TestValidateBuilderSubmissionV2(t *testing.T) { genesis, preMergeBlocks := generatePreMergeChain(20) os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) diff --git a/miner/contract_simulator_test.go b/miner/contract_simulator_test.go index dca1f75068..a917325f06 100644 --- a/miner/contract_simulator_test.go +++ b/miner/contract_simulator_test.go @@ -201,7 +201,7 @@ func TestSimulatorState(t *testing.T) { require.NoError(t, err) } - block, _, err := w.getSealingBlock(b.chain.CurrentBlock().Hash(), b.chain.CurrentHeader().Time+12, testAddress1, 0, common.Hash{}, nil, false, nil, nil) + block, _, err := w.getSealingBlock(b.chain.CurrentBlock().Hash(), b.chain.CurrentHeader().Time+12, testAddress1, 0, common.Hash{}, nil, false, nil, AssemblerTxLists{}) require.NoError(t, err) require.NotNil(t, block) if requireTx != -1 { diff --git a/miner/miner.go b/miner/miner.go index 623c2673c9..df8201cdf7 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -319,8 +319,3 @@ func (miner *Miner) PayloadAssembler(args *BuildPayloadArgs) (*Payload, error) { func (miner *Miner) BuildPayload(args *BuildPayloadArgs) (*Payload, error) { return miner.worker.buildPayload(args) } - -// BuildPayload builds the payload according to the provided parameters. -func (miner *Miner) BuildTobPayload(args *BuildPayloadArgs) (*Payload, error) { - return miner.worker.buildTobPayload(args) -} diff --git a/miner/multi_worker.go b/miner/multi_worker.go index 6940bb2b28..fdc225d45c 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -87,58 +87,6 @@ func (w *multiWorker) disablePreseal() { } } -func (w *multiWorker) buildTobPayload(args *BuildPayloadArgs) (*Payload, error) { - if args.ProposerCommitment != 2 { - log.Error("TOB payload building only built for TOB_ROB_SPLIT proposer_commitment") - return nil, errors.New("TOB payload building only built for TOB_ROB_SPLIT proposer_commitment") - } - log.Info("DEBUG: Starting to build TOB payload!!\n") - // Build the initial version with no transaction included. It should be fast - // enough to run. The empty payload can at least make sure there is something - // to deliver for not missing slot. - var empty *types.Block - for _, worker := range w.workers { - var err error - empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) - if err != nil { - log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) - continue - } - break - } - - if empty == nil { - return nil, errors.New("no worker could build an empty block") - } - - log.Info("DEBUG: Got empty TOB payload!!\n") - - // Construct a payload object for return. - payload := newPayload(empty, args.Id()) - - if len(w.workers) == 0 { - return payload, nil - } - - var tobBlock *types.Block - var fees *big.Int - for _, worker := range w.workers { - var err error - start := time.Now() - tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, true, args.BlockHook, args.TobBlockHook, nil) - if err != nil { - log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) - continue - } - log.Info("DEBUG: Got TOB payload!!\n") - log.Info("DEBUG: TOB Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") - payload.update(tobBlock, fees, time.Since(start)) - break - } - - return payload, nil -} - func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) { log.Info("DEBUG: Starting to assemble payload!!!!\n") // Build the initial version with no transaction included. It should be fast @@ -147,7 +95,7 @@ func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) var empty *types.Block for _, worker := range w.workers { var err error - empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) + empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, nil, AssemblerTxLists{}) if err != nil { log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) continue @@ -173,7 +121,7 @@ func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) for _, worker := range w.workers { var err error start := time.Now() - tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, true, args.BlockHook, args.TobBlockHook, args.AssemblerTxs) + tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) if err != nil { log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) continue @@ -195,7 +143,7 @@ func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { var empty *types.Block for _, worker := range w.workers { var err error - empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, nil, nil, nil) + empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, nil, AssemblerTxLists{}) if err != nil { log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) continue @@ -224,7 +172,7 @@ func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { go func(w *worker) { // Update routine done elsewhere! start := time.Now() - block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, false, args.BlockHook, args.TobBlockHook, nil) + block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, AssemblerTxLists{}) if err == nil { workerPayload.update(block, fees, time.Since(start)) } else { diff --git a/miner/payload_building.go b/miner/payload_building.go index cc7246518d..dda330f4af 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -31,20 +31,23 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +type AssemblerTxLists struct { + TobTxs []*types.Transaction + RobTxs *types.Transactions +} + // BuildPayloadArgs contains the provided parameters for building payload. // Check engine-api specification for more details. // https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1 type BuildPayloadArgs struct { - Parent common.Hash // The parent block to build payload on top - Timestamp uint64 // The provided timestamp of generated payload - FeeRecipient common.Address // The provided recipient address for collecting transaction fee - GasLimit uint64 - Random common.Hash // The provided randomness value - Withdrawals types.Withdrawals // The provided withdrawals - BlockHook BlockHookFn - TobBlockHook TobBlockHookFn - ProposerCommitment uint64 // The provided proposer commitment - AssemblerTxs map[common.Address]types.Transactions // The txs to be assembled + Parent common.Hash // The parent block to build payload on top + Timestamp uint64 // The provided timestamp of generated payload + FeeRecipient common.Address // The provided recipient address for collecting transaction fee + GasLimit uint64 + Random common.Hash // The provided randomness value + Withdrawals types.Withdrawals // The provided withdrawals + BlockHook BlockHookFn + AssemblerTxs AssemblerTxLists // The txs to be assembled } // Id computes an 8-byte identifier by hashing the components of the payload arguments. @@ -224,7 +227,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. - empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, false, args.BlockHook, args.TobBlockHook, nil) + empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, args.BlockHook, AssemblerTxLists{}) if err != nil { return nil, err } @@ -248,7 +251,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { select { case <-timer.C: start := time.Now() - block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, false, args.BlockHook, args.TobBlockHook, nil) + block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, AssemblerTxLists{}) if err == nil { payload.update(block, fees, time.Since(start)) } diff --git a/miner/verify_bundles.go b/miner/verify_bundles.go index 3cded68256..300e109b0b 100644 --- a/miner/verify_bundles.go +++ b/miner/verify_bundles.go @@ -113,10 +113,6 @@ func (e *ErrUnexpectedTx) Error() string { // 3. All txs from the bundle must be in the right order, gaps between txs are allowed // 4. All txs in the block are either from mempool or from the included bundles func VerifyBundlesAtomicity(env *environment, committedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, mempoolTxHashes map[common.Hash]struct{}) error { - // TOB blocks do not contain bundles - if env.isTob { - return nil - } // bundleHash -> tx includedBundles := make(bundleHashToTransactionDataMap). ExtractFromBundles(committedBundles). diff --git a/miner/worker.go b/miner/worker.go index 64bd120346..bef3b0bdae 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -95,21 +95,20 @@ var ( type environment struct { signer types.Signer - state *state.StateDB // apply state changes here - ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) - family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) - tcount int // tx count in cycle - gasPool *core.GasPool // available gas used to pack transactions - coinbase common.Address - profit *big.Int - isTob bool - isAssembler bool - - header *types.Header - txs []*types.Transaction - assemblerTxs map[common.Address]types.Transactions - receipts []*types.Receipt - uncles map[common.Hash]*types.Header + state *state.StateDB // apply state changes here + ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity) + family mapset.Set[common.Hash] // family set (used for checking uncle invalidity) + tcount int // tx count in cycle + gasPool *core.GasPool // available gas used to pack transactions + coinbase common.Address + profit *big.Int + isAssembler bool + assemblerTxs AssemblerTxLists + + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt + uncles map[common.Hash]*types.Header } // copy creates a deep copy of environment. @@ -124,7 +123,6 @@ func (env *environment) copy() *environment { profit: new(big.Int).Set(env.profit), header: types.CopyHeader(env.header), receipts: copyReceipts(env.receipts), - isTob: env.isTob, } if env.gasPool != nil { gasPool := *env.gasPool @@ -887,7 +885,7 @@ func (w *worker) resultLoop() { } // makeEnv creates a new environment for the sealing block. -func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address, isTob bool, assemblerTxs map[common.Address]types.Transactions) (*environment, error) { +func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address, assemblerTxs AssemblerTxLists) (*environment, error) { // Retrieve the parent state to execute on top and start a prefetcher for // the miner to speed block sealing up a bit. state, err := w.chain.StateAt(parent.Root) @@ -898,19 +896,18 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co // Note the passed coinbase may be different with header.Coinbase. env := &environment{ - signer: types.MakeSigner(w.chainConfig, header.Number), - state: state, - coinbase: coinbase, - ancestors: mapset.NewSet[common.Hash](), - family: mapset.NewSet[common.Hash](), - header: header, - uncles: make(map[common.Hash]*types.Header), - profit: new(big.Int), - isTob: isTob, - } - if assemblerTxs != nil && len(assemblerTxs) > 0 { + signer: types.MakeSigner(w.chainConfig, header.Number), + state: state, + coinbase: coinbase, + ancestors: mapset.NewSet[common.Hash](), + family: mapset.NewSet[common.Hash](), + header: header, + uncles: make(map[common.Hash]*types.Header), + profit: new(big.Int), + assemblerTxs: assemblerTxs, + } + if len(assemblerTxs.TobTxs) > 0 || (assemblerTxs.RobTxs != nil && assemblerTxs.RobTxs.Len() > 0) { env.isAssembler = true - env.assemblerTxs = assemblerTxs } // when 08 is processed ancestors contain 07 (quick block) for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { @@ -996,29 +993,16 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]* } } - if env.isTob { - log.Info("DEBUG: Applying TOB tx with nonce!\n", "tx", tx.Nonce()) - log.Info("DEBUG: Applying TOB tx!\n") - } receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, &gasPool, stateDB, env.header, tx, &envGasUsed, config, hook) if err != nil { - if env.isTob { - log.Info("DEBUG: ApplyTransaction failed with!\n", "err", err) - } stateDB.RevertToSnapshot(snapshot) return nil, err } - if env.isTob { - log.Info("DEBUG: ApplyTransaction succeeded!\n") - } *env.gasPool = gasPool env.header.GasUsed = envGasUsed env.state = stateDB - if env.isTob { - log.Info("DEBUG: Adding tx to env!\n") - } env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) @@ -1117,22 +1101,15 @@ func (w *worker) commitBundle(env *environment, txs types.Transactions, interrup return nil } -// TOB txs are picked out from the mempool by certain heuristics. It is highly likely that these txs will be nonce gapped -// due to this the tx commitment will never succeed. We also cannot pick txs in such a way that we avoid nonce gap as this will reduce -// the search space of txs which can be included in the TOB. -// To bypass this, we don't commit the TOB txs but simply send them to the relayer. The actual commitment of the TOB txs will -// happen in the relayer payload assembler which will assemble the TOB and ROB block -func (w *worker) mockCommitTobTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) error { - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs") - } +func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs AssemblerTxLists, interrupt *int32) error { gasLimit := env.header.GasLimit if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) } var coalescedLogs []*types.Log - for { + // first go thru TOB txs + for _, tx := range assemblerTxs.TobTxs { // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1144,72 +1121,96 @@ func (w *worker) mockCommitTobTransactions(env *environment, txs *types.Transact log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) break } - // Retrieve the next transaction and abort if all done - order := txs.Peek() - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs", "order", order) - } - if order == nil { - break - } - tx := order.Tx() - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs", "tx", tx) - } - if tx == nil { - continue - } + // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) + logs, err := w.commitTransaction(env, tx) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) - // Check whether the tx is replay protected. If we're not in the EIP155 hf - // phase, start ignoring the sender until we do. - if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { - log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txs.Pop() - continue + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + + case errors.Is(err, types.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + } + } + + // commit the ROB txs + i := 0 + for { + // Check interruption signal and abort building if it's fired. + if interrupt != nil { + if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { + return signalToErr(signal) + } + } + // If we don't have enough gas for any further transactions then we're done. + if env.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + break } - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs, we are commiting txs now") + if i >= assemblerTxs.RobTxs.Len() { + break } + + tx := assemblerTxs.RobTxs.Index(i) + + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + from, _ := types.Sender(env.signer, tx) logs, err := w.commitTransaction(env, tx) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account log.Trace("Gas limit exceeded for current block", "sender", from) - txs.Pop() case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txs.Shift() case errors.Is(err, core.ErrNonceTooHigh): // Reorg notification data race between the transaction pool and miner, skip account = log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) - txs.Pop() case errors.Is(err, nil): // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) env.tcount++ - txs.Shift() case errors.Is(err, types.ErrTxTypeNotSupported): // Pop the unsupported transaction without shifting in the next from the account log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) - txs.Pop() default: // Strange error, discard the transaction and get the next in line (note, the // nonce-too-high clause will prevent us from executing in vain). log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) - txs.Shift() } + + i += 1 } + if !w.isRunning() && len(coalescedLogs) > 0 { // We don't push the pendingLogsEvent while we are sealing. The reason is that // when we are sealing, the worker will regenerate a sealing block every 3 seconds. @@ -1229,9 +1230,6 @@ func (w *worker) mockCommitTobTransactions(env *environment, txs *types.Transact } func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32) error { - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs") - } gasLimit := env.header.GasLimit if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) @@ -1252,16 +1250,10 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP } // Retrieve the next transaction and abort if all done order := txs.Peek() - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs", "order", order) - } if order == nil { break } tx := order.Tx() - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs", "tx", tx) - } if tx == nil { continue } @@ -1278,9 +1270,6 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP continue } - if env.isTob { - log.Info("DEBUG: In Commiting TOB txs, we are commiting txs now") - } logs, err := w.commitTransaction(env, tx) switch { case errors.Is(err, core.ErrGasLimitReached): @@ -1336,20 +1325,18 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP // generateParams wraps various of settings for generating sealing task. type generateParams struct { - timestamp uint64 // The timstamp for sealing task - forceTime bool // Flag whether the given timestamp is immutable or not - parentHash common.Hash // Parent block hash, empty means the latest chain head - coinbase common.Address // The fee recipient address for including transaction - gasLimit uint64 // The validator's requested gas limit target - random common.Hash // The randomness generated by beacon chain, empty before the merge - withdrawals types.Withdrawals // List of withdrawals to include in block. - noUncle bool // Flag whether the uncle block inclusion is allowed - noTxs bool // Flag whether an empty block without any transaction is expected - onBlock BlockHookFn // Callback to call for each produced block - onTobBlock TobBlockHookFn // Callback to call for each produced tob block - isTobBlock bool // Are we generating a tob block or a normal/rob block - isAssembler bool // Are we generating a block as an assembler - assemblerTxs map[common.Address]types.Transactions // The transactions that the assembler wants to make a block out of + timestamp uint64 // The timstamp for sealing task + forceTime bool // Flag whether the given timestamp is immutable or not + parentHash common.Hash // Parent block hash, empty means the latest chain head + coinbase common.Address // The fee recipient address for including transaction + gasLimit uint64 // The validator's requested gas limit target + random common.Hash // The randomness generated by beacon chain, empty before the merge + withdrawals types.Withdrawals // List of withdrawals to include in block. + noUncle bool // Flag whether the uncle block inclusion is allowed + noTxs bool // Flag whether an empty block without any transaction is expected + onBlock BlockHookFn // Callback to call for each produced block + isAssembler bool // Are we generating a block as an assembler + assemblerTxs AssemblerTxLists // The transactions that the assembler wants to make a block out of } func doPrepareHeader(genParams *generateParams, chain *core.BlockChain, config *Config, chainConfig *params.ChainConfig, extra []byte, engine consensus.Engine) (*types.Header, *types.Header, error) { @@ -1426,7 +1413,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { // Could potentially happen if starting to mine in an odd state. // Note genParams.coinbase can be different with header.Coinbase // since clique algorithm can modify the coinbase field in header. - env, err := w.makeEnv(parent, header, genParams.coinbase, genParams.isTobBlock, genParams.assemblerTxs) + env, err := w.makeEnv(parent, header, genParams.coinbase, genParams.assemblerTxs) if err != nil { log.Error("Failed to create sealing context", "err", err) return nil, err @@ -1460,11 +1447,6 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) mempoolTxHashes map[common.Hash]struct{} err error ) - if env.isTob { - log.Info("DEBUG: Filling up TOB txs!!\n") - blockBundles, allBundles, mempoolTxHashes, err = w.fillTobTransactions(interrupt, env) - return blockBundles, allBundles, usedSbundles, mempoolTxHashes, err - } if env.isAssembler { log.Info("DEBUG: Actually assembling the txs!!\n") blockBundles, allBundles, mempoolTxHashes, err = w.fillAssemblerTransactions(interrupt, env) @@ -1490,106 +1472,34 @@ func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ( log.Info("DEBUG: In fillAssemblerTransactions!!!\n") assemblerTxs := env.assemblerTxs + totalTxsToAssemble := assemblerTxs.RobTxs.Len() + len(assemblerTxs.TobTxs) - if len(assemblerTxs) > 0 { - log.Info("DEBUG: Commiting assembler txs!!!\n") - txs := types.NewTransactionsByPriceAndNonce(env.signer, assemblerTxs, nil, nil, env.header.BaseFee) - if err := w.commitTransactions(env, txs, interrupt); err != nil { - return nil, nil, nil, err - } + mempoolHashes := make(map[common.Hash]struct{}, totalTxsToAssemble) + for _, tx := range assemblerTxs.TobTxs { + mempoolHashes[tx.Hash()] = struct{}{} } + i := 0 + for { + if i >= assemblerTxs.RobTxs.Len() { + break + } - log.Info("DEBUG: Assembler environment is ", "env", env) - - return []types.SimulatedBundle{}, []types.SimulatedBundle{}, map[common.Hash]struct{}{}, nil -} - -// fillTransactions retrieves the pending transactions from the txpool and fills them -// into the given sealing block. The transaction selection and ordering strategy can -// be customized with the plugin in the future. -// Returns error if any, otherwise the bundles that made it into the block and all bundles that passed simulation -func (w *worker) fillTobTransactions(interrupt *int32, env *environment) ([]types.SimulatedBundle, []types.SimulatedBundle, map[common.Hash]struct{}, error) { - log.Info("DEBUG: In fillTobTransactions!!!\n") - // Split the pending transactions into locals and remotes - // Fill the block with all available pending transactions. - pending := w.eth.TxPool().Pending(true) - mempoolTxHashes := make(map[common.Hash]struct{}, len(pending)) - // filter the txs for uniswap v2 router tx with the highest value - tobPending := make(map[common.Address]types.Transactions) - - log.Info("DEBUG: pending txs are \n", "pending", pending) + robTx := assemblerTxs.RobTxs.Index(i) + mempoolHashes[robTx.Hash()] = struct{}{} - type highestValueTx struct { - tx *types.Transaction - value *big.Int - from common.Address + i += 1 } - // store the highest value tx here - var highestValueTxTracker = highestValueTx{ - tx: nil, - value: big.NewInt(0), - from: common.Address{}, - } - // store the highest value tx from uniswap here - var highestValueUniswapTxTracker = highestValueTx{ - tx: nil, - value: big.NewInt(0), - from: common.Address{}, - } - for account, txs := range pending { - for _, tx := range txs { - if tx.Value().Cmp(highestValueTxTracker.value) == 1 { - log.Info("DEBUG: tx.Value() is \n", "tx.Value()", tx.Value()) - log.Info("DEBUG: tx.To() is \n", "tx.To()", *tx.To()) - log.Info("DEBUG: tx.From() is \n", "tx.From()", account) - log.Info("DEBUG: tx.Nonce() is \n", "tx.Nonce()", tx.Nonce()) - highestValueTxTracker.tx = tx - highestValueTxTracker.value = tx.Value() - highestValueTxTracker.from = account - } - if tx.To() != nil { - log.Info("DEBUG: tx.To() is \n", "tx.To()", *tx.To()) - log.Info("DEBUG: tx.Value() is \n", "tx.Value()", tx.Value()) - log.Info("DEBUG: tx.From() is \n", "tx.From()", account) - log.Info("DEBUG: tx.Nonce() is \n", "tx.Nonce()", tx.Nonce()) - if *tx.To() == common.HexToAddress("0xB9D7a3554F221B34f49d7d3C61375E603aFb699e") { - log.Info("DEBUG: Got a uniswap v2 tx!!!\n") - if tx.Value().Cmp(highestValueUniswapTxTracker.value) >= 0 { - highestValueUniswapTxTracker.tx = tx - highestValueUniswapTxTracker.value = tx.Value() - highestValueUniswapTxTracker.from = account - } - } - } + if totalTxsToAssemble > 0 { + log.Info("DEBUG: Commiting assembler txs!!!\n") + if err := w.commitAssemblyTransactions(env, env.assemblerTxs, interrupt); err != nil { + return nil, nil, nil, err } } - log.Info("DEBUG: highestValueTxTracker is \n", "highestValueTxTracker", highestValueTxTracker) - log.Info("DEBUG: highestValueUniswapTxTracker is \n", "highestValueUniswapTxTracker", highestValueUniswapTxTracker) - - if highestValueUniswapTxTracker.tx != nil { - env.txs = append(env.txs, highestValueUniswapTxTracker.tx) - env.tcount++ - //tobPending[highestValueUniswapTxTracker.from] = append(tobPending[highestValueUniswapTxTracker.from], highestValueUniswapTxTracker.tx) - } else if highestValueTxTracker.tx != nil { - env.txs = append(env.txs, highestValueTxTracker.tx) - env.tcount++ - //tobPending[highestValueTxTracker.from] = append(tobPending[highestValueTxTracker.from], highestValueTxTracker.tx) - } - log.Info("DEBUG: env post TOB tx checking is!\n", "env", env) - //log.Info("DEBUG: tobPending is \n", "tobPending", tobPending) - if len(tobPending) > 0 { - log.Info("DEBUG: Commiting TOB txs!!!\n") - //txs := types.NewTransactionsByPriceAndNonce(env.signer, tobPending, nil, nil, env.header.BaseFee) - //if err := w.commitTransactions(env, txs, interrupt); err != nil { - // return nil, nil, nil, err - //} - } - - log.Info("DEBUG: TOB environment is ", "env", env) + log.Info("DEBUG: Assembler environment is ", "env", env) - return []types.SimulatedBundle{}, []types.SimulatedBundle{}, mempoolTxHashes, nil + return []types.SimulatedBundle{}, []types.SimulatedBundle{}, mempoolHashes, nil } // fillTransactions retrieves the pending transactions from the txpool and fills them @@ -1616,7 +1526,6 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) ([]types.S var blockBundles []types.SimulatedBundle var allBundles []types.SimulatedBundle - // TOB block will not hold any bundles if w.flashbots.isFlashbots { bundles, ccBundleCh := w.eth.TxPool().MevBundles(env.header.Number, env.header.Time) @@ -1775,7 +1684,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e finalizeFn := func(env *environment, orderCloseTime time.Time, blockBundles []types.SimulatedBundle, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, noTxs bool) (*types.Block, *big.Int, error) { - block, profit, err := w.finalizeBlock(env, params.withdrawals, validatorCoinbase, noTxs, params.isTobBlock) + block, profit, err := w.finalizeBlock(env, params.withdrawals, validatorCoinbase, noTxs) if err != nil { log.Error("could not finalize block", "err", err) return nil, nil, err @@ -1801,10 +1710,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e transactionNumGauge.Update(int64(len(env.txs))) } if !params.isAssembler { - if params.isTobBlock && params.onTobBlock != nil { - log.Info("DEBUG: Running TOB block hook!!\n") - go params.onTobBlock(block, profit) - } else if params.onBlock != nil { + if params.onBlock != nil { log.Info("DEBUG: Running ROB block hook!!\n") go params.onBlock(block, profit, orderCloseTime, blockBundles, allBundles, usedSbundles) } @@ -1817,18 +1723,15 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, time.Now(), nil, nil, nil, true) } - // TODO - Add tob validator payment support + // TODO - Add builder payment support for TOB_ROB_SPLIT //var paymentTxReserve *proposerTxReservation - //if !params.isTobBlock || !params.isAssembler { - // paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) - // if err != nil { - // return nil, nil, err - // } + //paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) + //if err != nil { + // return nil, nil, err //} orderCloseTime := time.Now() - log.Info("DEBUG: env isTob", "isTob", work.isTob) blockBundles, allBundles, usedSbundles, mempoolTxHashes, err := w.fillTransactionsSelectAlgo(nil, work) if err != nil { return nil, nil, err @@ -1857,17 +1760,15 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, true) } - //if !params.isTobBlock || !params.isAssembler { - // err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) - // if err != nil { - // return nil, nil, err - // } + //err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) + //if err != nil { + // return nil, nil, err //} return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, false) } -func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, validatorCoinbase common.Address, noTxs bool, isTobBlock bool) (*types.Block, *big.Int, error) { +func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, validatorCoinbase common.Address, noTxs bool) (*types.Block, *big.Int, error) { block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, withdrawals) if err != nil { return nil, nil, err @@ -1881,16 +1782,12 @@ func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, return block, big.NewInt(0), nil } - var blockProfit = big.NewInt(0) - // TODO - support proposer payments for TOB - //if !isTobBlock { - // blockProfit, err = w.checkProposerPayment(work, validatorCoinbase) - // if err != nil { - // return nil, nil, err - // } + //blockProfit, err := w.checkProposerPayment(work, validatorCoinbase) + //if err != nil { + // return nil, nil, err //} - return block, blockProfit, nil + return block, big.NewInt(10), nil } func (w *worker) checkProposerPayment(work *environment, validatorCoinbase common.Address) (*big.Int, error) { @@ -2020,7 +1917,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti // getSealingBlock generates the sealing block based on the given parameters. // The generation result will be passed back via the given channel no matter // the generation itself succeeds or not. -func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, gasLimit uint64, random common.Hash, withdrawals types.Withdrawals, noTxs bool, isTobBlock bool, blockHook BlockHookFn, tobBlockHook TobBlockHookFn, assemblerTxs map[common.Address]types.Transactions) (*types.Block, *big.Int, error) { +func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, gasLimit uint64, random common.Hash, withdrawals types.Withdrawals, noTxs bool, blockHook BlockHookFn, assemblerTxs AssemblerTxLists) (*types.Block, *big.Int, error) { req := &getWorkReq{ params: &generateParams{ timestamp: timestamp, @@ -2033,8 +1930,6 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase noUncle: true, noTxs: noTxs, onBlock: blockHook, - onTobBlock: tobBlockHook, - isTobBlock: isTobBlock, assemblerTxs: assemblerTxs, }, result: make(chan *newPayloadResult, 1), diff --git a/miner/worker_test.go b/miner/worker_test.go index 06da3db3e5..93b746920a 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -658,7 +658,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is not enabled for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, true, false, nil, nil, nil) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, true, nil, AssemblerTxLists{}) if c.expectErr { if err == nil { t.Error("Expect error but get nil") @@ -674,7 +674,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co // This API should work even when the automatic sealing is enabled w.start() for _, c := range cases { - block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, false, false, nil, nil, nil) + block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, 0, c.random, nil, false, nil, AssemblerTxLists{}) if c.expectErr { if err == nil { t.Error("Expect error but get nil") @@ -822,7 +822,7 @@ func testBundles(t *testing.T) { require.NoError(t, err) } - block, _, err := w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, false, nil, nil, nil) + block, _, err := w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, nil, AssemblerTxLists{}) require.NoError(t, err) state, err := w.chain.State() @@ -837,3 +837,120 @@ func testBundles(t *testing.T) { t.Log("Balances", balancePre, balancePost) } } + +func TestBlockAssembly(t *testing.T) { + db := rawdb.NewMemoryDatabase() + chainConfig := params.AllEthashProtocolChanges + engine := ethash.NewFaker() + + chainConfig.LondonBlock = big.NewInt(0) + + genesisAlloc := core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}} + + nExtraKeys := 5 + extraKeys := make([]*ecdsa.PrivateKey, nExtraKeys) + for i := 0; i < nExtraKeys; i++ { + pk, _ := crypto.GenerateKey() + address := crypto.PubkeyToAddress(pk.PublicKey) + extraKeys[i] = pk + genesisAlloc[address] = core.GenesisAccount{Balance: testBankFunds} + } + + nSearchers := 5 + searcherPrivateKeys := make([]*ecdsa.PrivateKey, nSearchers) + for i := 0; i < nSearchers; i++ { + pk, _ := crypto.GenerateKey() + address := crypto.PubkeyToAddress(pk.PublicKey) + searcherPrivateKeys[i] = pk + genesisAlloc[address] = core.GenesisAccount{Balance: testBankFunds} + } + + for _, address := range []common.Address{testAddress1, testAddress2, testAddress3} { + genesisAlloc[address] = core.GenesisAccount{Balance: testBankFunds} + } + + w, b := newTestWorker(t, chainConfig, engine, db, genesisAlloc, 0) + defer w.close() + + // Ignore empty commit here for less noise. + w.skipSealHook = func(task *task) bool { + return len(task.receipts) == 0 + } + + // Test 1 + tobTxs := []*types.Transaction{ + b.newRandomTx(false, testBankAddress, 1e15, testAddress1Key, 0, big.NewInt(100*params.InitialBaseFee)), + b.newRandomTx(false, testBankAddress, 1e15, testAddress2Key, 0, big.NewInt(110*params.InitialBaseFee)), + b.newRandomTx(false, testBankAddress, 1e15, testAddress3Key, 0, big.NewInt(120*params.InitialBaseFee)), + } + + robTxs := make([]*types.Transaction, len(searcherPrivateKeys)) + for _, pk := range searcherPrivateKeys { + robTxs = append(robTxs, b.newRandomTx(false, testBankAddress, 1, pk, 0, big.NewInt(150*params.InitialBaseFee))) + } + finalRobTxs := types.Transactions{} + for _, tx := range robTxs { + if tx != nil { + finalRobTxs = append(finalRobTxs, tx) + } + } + + aTxs := AssemblerTxLists{ + TobTxs: tobTxs, + RobTxs: &finalRobTxs, + } + + block, _, err := w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, nil, aTxs) + require.NoError(t, err) + txs := block.Transactions() + mergedTobAndRobTxs := types.Transactions{} + for _, tx := range aTxs.TobTxs { + mergedTobAndRobTxs = append(mergedTobAndRobTxs, tx) + } + for _, tx := range *aTxs.RobTxs { + mergedTobAndRobTxs = append(mergedTobAndRobTxs, tx) + } + require.Equal(t, txs, mergedTobAndRobTxs) + + // Test 2 - No TOB txs but only ROB txs + robTxs = make([]*types.Transaction, len(searcherPrivateKeys)) + for _, pk := range searcherPrivateKeys { + robTxs = append(robTxs, b.newRandomTx(false, testBankAddress, 1, pk, 0, big.NewInt(150*params.InitialBaseFee))) + } + finalRobTxs = types.Transactions{} + for _, tx := range robTxs { + if tx != nil { + finalRobTxs = append(finalRobTxs, tx) + } + } + + aTxs = AssemblerTxLists{ + TobTxs: []*types.Transaction{}, + RobTxs: &finalRobTxs, + } + block, _, err = w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, nil, aTxs) + require.NoError(t, err) + txs = block.Transactions() + require.Equal(t, txs, finalRobTxs) + + // Test 3 - no ROB txs but only TOB txs + tobTxs = []*types.Transaction{ + b.newRandomTx(false, testBankAddress, 1e15, testAddress1Key, 0, big.NewInt(100*params.InitialBaseFee)), + b.newRandomTx(false, testBankAddress, 1e15, testAddress2Key, 0, big.NewInt(110*params.InitialBaseFee)), + b.newRandomTx(false, testBankAddress, 1e15, testAddress3Key, 0, big.NewInt(120*params.InitialBaseFee)), + } + + aTxs = AssemblerTxLists{ + TobTxs: tobTxs, + RobTxs: &types.Transactions{}, + } + block, _, err = w.getSealingBlock(w.chain.CurrentBlock().Hash(), w.chain.CurrentHeader().Time+12, testUserAddress, 0, common.Hash{}, nil, false, nil, aTxs) + require.NoError(t, err) + txs = block.Transactions() + mergedTobTxs := types.Transactions{} + for _, tx := range aTxs.TobTxs { + mergedTobTxs = append(mergedTobTxs, tx) + } + require.Equal(t, txs, mergedTobTxs) + +} From 12fab1b5c8d1b98dd387b1577ed482775a5c2bd2 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 01:35:29 +0530 Subject: [PATCH 04/19] work --- eth/block-validation/api.go | 33 +++++++++++++++++++++++++++++---- miner/worker.go | 29 ++++++++++++++--------------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 695c238f49..15c879ef19 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -12,6 +12,7 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" + bellatrixUtil "github.com/attestantio/go-eth2-client/util/bellatrix" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -233,12 +234,36 @@ func (r *BuilderBlockValidationRequestV2) UnmarshalJSON(data []byte) error { } type BlockAssemblerRequest struct { - TobTxs [][]byte `json:"tob_txs"` + TobTxs bellatrixUtil.ExecutionPayloadTransactions + RobPayload *capellaapi.SubmitBlockRequest + RegisteredGasLimit uint64 + PayloadAttributes *engine.PayloadAttributes +} + +type IntermediateBlockAssemblerRequest struct { + TobTxs []byte `json:"tob_txs"` RobPayload *capellaapi.SubmitBlockRequest `json:"rob_payload"` RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` PayloadAttributes *engine.PayloadAttributes `json:"payload_attributes"` } +func (b *BlockAssemblerRequest) UnmarshalJSON(data []byte) error { + var intermediateJson IntermediateBlockAssemblerRequest + err := json.Unmarshal(data, &intermediateJson) + if err != nil { + return err + } + err = b.TobTxs.UnmarshalSSZ(intermediateJson.TobTxs) + if err != nil { + return err + } + b.RegisteredGasLimit = intermediateJson.RegisteredGasLimit + b.PayloadAttributes = intermediateJson.PayloadAttributes + b.RobPayload = intermediateJson.RobPayload + + return nil +} + // bchain: copied this here to avoid circular dependency func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*capella.ExecutionPayload, error) { transactionData := make([]bellatrix.Transaction, len(data.Transactions)) @@ -287,9 +312,9 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c } log.Info("DEBUG: Entered the BlockAssembler!!") - log.Info("BlockAssembler", "tobTxs", len(params.TobTxs), "robPayload", params.RobPayload) - transactionBytes := make([][]byte, len(params.TobTxs)) - for i, txHexBytes := range params.TobTxs { + log.Info("BlockAssembler", "tobTxs", len(params.TobTxs.Transactions), "robPayload", params.RobPayload) + transactionBytes := make([][]byte, len(params.TobTxs.Transactions)) + for i, txHexBytes := range params.TobTxs.Transactions { transactionBytes[i] = txHexBytes[:] } txs, err := engine.DecodeTransactions(transactionBytes) diff --git a/miner/worker.go b/miner/worker.go index bef3b0bdae..b04219d013 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1723,12 +1723,11 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, time.Now(), nil, nil, nil, true) } - // TODO - Add builder payment support for TOB_ROB_SPLIT - //var paymentTxReserve *proposerTxReservation - //paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) - //if err != nil { - // return nil, nil, err - //} + var paymentTxReserve *proposerTxReservation + paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) + if err != nil { + return nil, nil, err + } orderCloseTime := time.Now() @@ -1760,10 +1759,10 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, true) } - //err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) - //if err != nil { - // return nil, nil, err - //} + err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) + if err != nil { + return nil, nil, err + } return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, false) } @@ -1782,12 +1781,12 @@ func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, return block, big.NewInt(0), nil } - //blockProfit, err := w.checkProposerPayment(work, validatorCoinbase) - //if err != nil { - // return nil, nil, err - //} + blockProfit, err := w.checkProposerPayment(work, validatorCoinbase) + if err != nil { + return nil, nil, err + } - return block, big.NewInt(10), nil + return block, blockProfit, nil } func (w *worker) checkProposerPayment(work *environment, validatorCoinbase common.Address) (*big.Int, error) { From 35bb4ae47e933fbf05f37942a939107ab45c83cb Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 01:39:52 +0530 Subject: [PATCH 05/19] code --- builder/builder.go | 9 ++++----- builder/builder_test.go | 7 +++---- builder/eth_service_test.go | 24 ------------------------ builder/relay.go | 16 +++++++--------- builder/relay_aggregator_test.go | 22 ++++++++++++++++++++++ 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index c9cc6471d7..cf0541de5c 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -40,10 +40,9 @@ const ( type PubkeyHex string type ValidatorData struct { - Pubkey PubkeyHex - FeeRecipient bellatrix.ExecutionAddress - GasLimit uint64 - ProposerCommitment uint64 + Pubkey PubkeyHex + FeeRecipient bellatrix.ExecutionAddress + GasLimit uint64 } type IRelay interface { @@ -434,7 +433,7 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. log.Info("DEBUG: In submit best block!") log.Info("DEBUG: Submitting ROB block!!", "isRobBlock", true) queueMu.Lock() - if queueBestEntry.block != nil && queueBestEntry.block.Hash() != queueLastSubmittedHash { + if queueBestEntry.block.Hash() != queueLastSubmittedHash { err := b.onSealedBlock(queueBestEntry.block, queueBestEntry.blockValue, queueBestEntry.ordersCloseTime, queueBestEntry.sealedAt, queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs, true) diff --git a/builder/builder_test.go b/builder/builder_test.go index 3f132d7be2..7c8c4cca9e 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -36,10 +36,9 @@ func TestOnPayloadAttributes(t *testing.T) { feeRecipient, _ := utils.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc00") testRelay := testRelay{ gvsVd: ValidatorData{ - Pubkey: PubkeyHex(testBeacon.validator.Pk.String()), - FeeRecipient: feeRecipient, - GasLimit: 10, - ProposerCommitment: 1, + Pubkey: PubkeyHex(testBeacon.validator.Pk.String()), + FeeRecipient: feeRecipient, + GasLimit: 10, }, } diff --git a/builder/eth_service_test.go b/builder/eth_service_test.go index ff3ed7cc23..b1f3dff65c 100644 --- a/builder/eth_service_test.go +++ b/builder/eth_service_test.go @@ -76,30 +76,6 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) return n, ethservice } -func TestBuildBlockUnsupportedProposerCommitment(t *testing.T) { - genesis, blocks := generatePreMergeChain(10) - n, ethservice := startEthService(t, genesis, blocks) - defer n.Close() - - parent := ethservice.BlockChain().CurrentBlock() - - testPayloadAttributes := &types.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) - service.eth.APIBackend.Miner().SetEtherbase(common.Address{0x05, 0x11}) - - err := service.BuildBlock(testPayloadAttributes, func(block *types.Block, blockValue *big.Int, _ time.Time, _, _ []types.SimulatedBundle, _ []types.UsedSBundle) { - }) - - require.ErrorContainsf(t, err, "TOB_ROB_SPLIT not supported yet", "expected error") -} - func TestBuildBlock(t *testing.T) { genesis, blocks := generatePreMergeChain(10) n, ethservice := startEthService(t, genesis, blocks) diff --git a/builder/relay.go b/builder/relay.go index be9f61b9bb..4c7dcca122 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -53,11 +53,10 @@ type GetValidatorRelayResponse []struct { Slot uint64 `json:"slot,string"` Entry struct { Message struct { - FeeRecipient string `json:"fee_recipient"` - GasLimit uint64 `json:"gas_limit,string"` - Timestamp uint64 `json:"timestamp,string"` - Pubkey string `json:"pubkey"` - ProposerCommitment uint64 `json:"proposer_commitment,string"` + FeeRecipient string `json:"fee_recipient"` + GasLimit uint64 `json:"gas_limit,string"` + Timestamp uint64 `json:"timestamp,string"` + Pubkey string `json:"pubkey"` } `json:"message"` Signature string `json:"signature"` } `json:"entry"` @@ -324,10 +323,9 @@ func (r *RemoteRelay) getSlotValidatorMapFromRelay() (map[uint64]ValidatorData, pubkeyHex := PubkeyHex(strings.ToLower(data.Entry.Message.Pubkey)) res[data.Slot] = ValidatorData{ - Pubkey: pubkeyHex, - FeeRecipient: feeRecipient, - GasLimit: data.Entry.Message.GasLimit, - ProposerCommitment: data.Entry.Message.ProposerCommitment, + Pubkey: pubkeyHex, + FeeRecipient: feeRecipient, + GasLimit: data.Entry.Message.GasLimit, } } diff --git a/builder/relay_aggregator_test.go b/builder/relay_aggregator_test.go index d46533e49a..1d7de6f882 100644 --- a/builder/relay_aggregator_test.go +++ b/builder/relay_aggregator_test.go @@ -57,6 +57,17 @@ func (r *testRelay) SubmitBlock(msg *bellatrix.SubmitBlockRequest, registration return r.sbError } +func (r *testRelay) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, registration ValidatorData) error { + if r.submittedMsgCh != nil { + select { + case r.submittedMsgCh <- msg: + default: + } + } + r.submittedMsg = msg + return r.sbError +} + func (r *testRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, registration ValidatorData) error { if r.submittedMsgCh != nil { select { @@ -68,6 +79,17 @@ func (r *testRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, registra return r.sbError } +func (r *testRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, registration ValidatorData) error { + if r.submittedMsgCh != nil { + select { + case r.submittedMsgChCapella <- msg: + default: + } + } + r.submittedMsgCapella = msg + return r.sbError +} + func (r *testRelay) GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) { r.requestedSlot = nextSlot return r.gvsVd, r.gvsErr From 83633dec684a182e53106bf44715e458eec8ee36 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 10:55:05 +0530 Subject: [PATCH 06/19] some updates --- builder/eth_service.go | 5 ++-- builder/local_relay.go | 10 ------- builder/relay.go | 53 ------------------------------------- builder/relay_aggregator.go | 10 ------- 4 files changed, 2 insertions(+), 76 deletions(-) diff --git a/builder/eth_service.go b/builder/eth_service.go index 777d2fd6de..349de61a04 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -84,15 +84,14 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal select { case payload := <-resCh: if payload == nil { - return errors.New("received nil tob payload from sealing work") + return errors.New("received nil payload from sealing work") } + return nil case <-timer.C: payload.Cancel() log.Error("timeout waiting for block", "parent hash", attrs.HeadHash, "slot", attrs.Slot) return errors.New("timeout waiting for block result") } - - return nil } func (s *EthereumService) GetBlockByHash(hash common.Hash) *types.Block { diff --git a/builder/local_relay.go b/builder/local_relay.go index 9a099b0ded..21c12c3846 100644 --- a/builder/local_relay.go +++ b/builder/local_relay.go @@ -66,21 +66,11 @@ type LocalRelay struct { fd ForkData } -func (r *LocalRelay) SubmitTobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func (r *LocalRelay) SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me panic("implement me") } -func (r *LocalRelay) SubmitTobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func (r *LocalRelay) SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me panic("implement me") diff --git a/builder/relay.go b/builder/relay.go index 4c7dcca122..d794276827 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -157,24 +157,6 @@ func (r *RemoteRelay) SubmitBlock(msg *bellatrix.SubmitBlockRequest, _ Validator return nil } -func (r *RemoteRelay) SubmitTobBlock(msg *bellatrix.SubmitBlockRequest, _ ValidatorData) error { - log.Info("submitting tob block to remote relay", "endpoint", r.config.Endpoint) - endpoint := r.config.Endpoint + "/relay/v1/builder/tob_blocks" - code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - - if r.localRelay != nil { - r.localRelay.submitBlock(msg) - } - - return nil -} - func (r *RemoteRelay) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, _ ValidatorData) error { log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) endpoint := r.config.Endpoint + "/relay/v1/builder/rob_blocks" @@ -231,41 +213,6 @@ func (r *RemoteRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, _ Vali return nil } -func (r *RemoteRelay) SubmitTobBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { - log.Info("submitting block to remote relay", "endpoint", r.config.Endpoint) - - endpoint := r.config.Endpoint + "/relay/v1/builder/tob_blocks" - - if r.config.SszEnabled { - bodyBytes, err := msg.MarshalSSZ() - if err != nil { - return fmt.Errorf("error marshaling ssz: %w", err) - } - log.Debug("submitting block to remote relay", "endpoint", r.config.Endpoint) - code, err := SendSSZRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, bodyBytes, r.config.GzipEnabled) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - } else { - code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - } - - if r.localRelay != nil { - r.localRelay.submitBlockCapella(msg) - } - - return nil -} - func (r *RemoteRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) diff --git a/builder/relay_aggregator.go b/builder/relay_aggregator.go index 2385042c7d..57f4d43ed8 100644 --- a/builder/relay_aggregator.go +++ b/builder/relay_aggregator.go @@ -18,21 +18,11 @@ type RemoteRelayAggregator struct { registrationsCache map[ValidatorData][]IRelay } -func (r *RemoteRelayAggregator) SubmitTobBlock(msg *bellatrix.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func (r *RemoteRelayAggregator) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me panic("implement me") } -func (r *RemoteRelayAggregator) SubmitTobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func (r *RemoteRelayAggregator) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me panic("implement me") From 17c47ca74f36cb23f17d722806d00f7b39212cbf Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 15:07:40 +0530 Subject: [PATCH 07/19] don't add payouts for assembled blocks --- miner/worker.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index b04219d013..4b0376a323 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1724,9 +1724,12 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e } var paymentTxReserve *proposerTxReservation - paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) - if err != nil { - return nil, nil, err + // there won't be any additional payments for a block assembled by the assembler + if !params.isAssembler { + paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) + if err != nil { + return nil, nil, err + } } orderCloseTime := time.Now() @@ -1756,12 +1759,18 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e // no bundles or tx from mempool if len(work.txs) == 0 { + log.Info("DEBUG: Got no txs from the mempool!") return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, true) } - err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) - if err != nil { - return nil, nil, err + // log out no of bundles + log.Info("DEBUG: Got bundles from the mempool!", "bundles", len(blockBundles)) + + if !params.isAssembler { + err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) + if err != nil { + return nil, nil, err + } } return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, false) @@ -1781,9 +1790,12 @@ func (w *worker) finalizeBlock(work *environment, withdrawals types.Withdrawals, return block, big.NewInt(0), nil } - blockProfit, err := w.checkProposerPayment(work, validatorCoinbase) - if err != nil { - return nil, nil, err + blockProfit := big.NewInt(0) + if !work.isAssembler { + blockProfit, err = w.checkProposerPayment(work, validatorCoinbase) + if err != nil { + return nil, nil, err + } } return block, blockProfit, nil @@ -2343,6 +2355,9 @@ func (w *worker) proposerTxCommit(env *environment, validatorCoinbase *common.Ad w.mu.Unlock() builderBalance := env.state.GetBalance(sender) + log.Info("DEBUG: builder current balance is \n", "builderBalance", builderBalance) + log.Info("DEBUG: builder reserved balance is \n", "reserveBalance", reserve.builderBalance) + availableFunds := new(big.Int).Sub(builderBalance, reserve.builderBalance) if availableFunds.Sign() <= 0 { return errors.New("builder balance decreased") From a5247ae3cf2b021af5ecf3b83f4efce1d5e97650 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 18:11:40 +0530 Subject: [PATCH 08/19] tests + fixes --- eth/block-validation/api.go | 29 ++- eth/block-validation/api_test.go | 344 ++++++++++++++++++++++++++++--- miner/multi_worker.go | 22 +- miner/worker.go | 18 +- 4 files changed, 348 insertions(+), 65 deletions(-) diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 15c879ef19..404cade0d2 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -235,16 +235,14 @@ func (r *BuilderBlockValidationRequestV2) UnmarshalJSON(data []byte) error { type BlockAssemblerRequest struct { TobTxs bellatrixUtil.ExecutionPayloadTransactions - RobPayload *capellaapi.SubmitBlockRequest + RobPayload capellaapi.SubmitBlockRequest RegisteredGasLimit uint64 - PayloadAttributes *engine.PayloadAttributes } type IntermediateBlockAssemblerRequest struct { - TobTxs []byte `json:"tob_txs"` - RobPayload *capellaapi.SubmitBlockRequest `json:"rob_payload"` - RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` - PayloadAttributes *engine.PayloadAttributes `json:"payload_attributes"` + TobTxs []byte `json:"tob_txs"` + RobPayload capellaapi.SubmitBlockRequest `json:"rob_payload"` + RegisteredGasLimit uint64 `json:"registered_gas_limit,string"` } func (b *BlockAssemblerRequest) UnmarshalJSON(data []byte) error { @@ -258,7 +256,6 @@ func (b *BlockAssemblerRequest) UnmarshalJSON(data []byte) error { return err } b.RegisteredGasLimit = intermediateJson.RegisteredGasLimit - b.PayloadAttributes = intermediateJson.PayloadAttributes b.RobPayload = intermediateJson.RobPayload return nil @@ -307,10 +304,6 @@ func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*cape } func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*capella.ExecutionPayload, error) { - if params.RobPayload == nil { - return nil, errors.New("nil rob payload") - } - log.Info("DEBUG: Entered the BlockAssembler!!") log.Info("BlockAssembler", "tobTxs", len(params.TobTxs.Transactions), "robPayload", params.RobPayload) transactionBytes := make([][]byte, len(params.TobTxs.Transactions)) @@ -352,13 +345,15 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c log.Info("Done Checking for duplicate txs!") withdrawals := make(types.Withdrawals, len(params.RobPayload.ExecutionPayload.Withdrawals)) - for _, withdrawal := range params.RobPayload.ExecutionPayload.Withdrawals { - withdrawals = withdrawals.Append(types.Withdrawal{ + log.Info("DEBUG: Building the final set of withdrawals") + log.Info("DEBUG: Withdrawals are ", "withdrawals", params.RobPayload.ExecutionPayload.Withdrawals) + for i, withdrawal := range params.RobPayload.ExecutionPayload.Withdrawals { + withdrawals[i] = &types.Withdrawal{ Index: uint64(withdrawal.Index), Validator: uint64(withdrawal.ValidatorIndex), Address: common.Address(withdrawal.Address), Amount: uint64(withdrawal.Amount), - }) + } } log.Info("DEBUG: Built the final set of withdrawals") @@ -373,9 +368,9 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c Parent: common.Hash(params.RobPayload.ExecutionPayload.ParentHash), Timestamp: params.RobPayload.ExecutionPayload.Timestamp, // TODO - this should be relayer fee recipient. We will implement payouts later - FeeRecipient: common.Address(params.RobPayload.ExecutionPayload.FeeRecipient), + FeeRecipient: common.Address(params.RobPayload.Message.ProposerFeeRecipient), GasLimit: params.RegisteredGasLimit, - Random: params.PayloadAttributes.Random, + Random: params.RobPayload.ExecutionPayload.PrevRandao, Withdrawals: withdrawals, BlockHook: nil, AssemblerTxs: miner.AssemblerTxLists{ @@ -387,6 +382,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c if err != nil { return nil, err } + log.Info("DEBUG: Resolving block!!") resolvedBlock := block.ResolveFull() if resolvedBlock == nil { return nil, errors.New("unable to resolve block") @@ -395,6 +391,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c return nil, errors.New("nil execution payload") } + log.Info("DEBUG: Resolved block!!") finalPayload, err := executableDataToCapellaExecutionPayload(resolvedBlock.ExecutionPayload) log.Info("DEBUG: Final payload", "finalPayload", finalPayload) diff --git a/eth/block-validation/api_test.go b/eth/block-validation/api_test.go index 1dd0f59176..c6b6f3c79d 100644 --- a/eth/block-validation/api_test.go +++ b/eth/block-validation/api_test.go @@ -15,6 +15,7 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" + bellatrixUtil "github.com/attestantio/go-eth2-client/util/bellatrix" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -44,10 +45,12 @@ import ( var ( // testKey is a private key to use for funding a tester account. - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testSearcherKey, _ = crypto.HexToECDSA("b61c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") // testAddr is the Ethereum address of the tester account. - testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + testSearcherAddr = crypto.PubkeyToAddress(testSearcherKey.PublicKey) testValidatorKey, _ = crypto.HexToECDSA("28c3cd61b687fdd03488e167a5d84f50269df2a4c29a2cfb1390903aa775c5d0") testValidatorAddr = crypto.PubkeyToAddress(testValidatorKey.PublicKey) @@ -170,35 +173,312 @@ func TestValidateBuilderSubmissionV1(t *testing.T) { require.ErrorContains(t, api.ValidateBuilderSubmissionV1(blockRequest), "could not apply tx 4", "insufficient funds for gas * price + value") } -// -//func TestBlockAssembler(t *testing.T) { -// genesis, preMergeBlocks := generatePreMergeChain(20) -// os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) -// time := preMergeBlocks[len(preMergeBlocks)-1].Time() + 5 -// genesis.Config.ShanghaiTime = &time -// n, ethservice := startEthService(t, genesis, preMergeBlocks) -// ethservice.Merger().ReachTTD() -// defer n.Close() -// -// api := NewBlockValidationAPI(ethservice, nil, false) -// parent := preMergeBlocks[len(preMergeBlocks)-1] -// -// api.eth.APIBackend.Miner().SetEtherbase(testBuilderAddr) -// -// statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) -// nonce := statedb.GetNonce(testAddr) -// -// signer := types.LatestSigner(ethservice.BlockChain().Config()) -// -// tobTx1, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x16}, big.NewInt(10), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) -// tobTx2, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x17}, big.NewInt(20), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) -// -// cc, _ := types.SignTx(types.NewContractCreation(nonce+1, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), signer, testKey) -// -// baseFee := misc.CalcBaseFee(params.AllEthashProtocolChanges, preMergeBlocks[len(preMergeBlocks)-1].Header()) -// tx2, _ := types.SignTx(types.NewTransaction(nonce+2, testAddr, big.NewInt(10), 21000, baseFee, nil), signer, testKey) -// -//} +func TestBlockAssemblerWithNoRobTxs(t *testing.T) { + genesis, preMergeBlocks := generatePreMergeChain(20) + os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) + time := preMergeBlocks[len(preMergeBlocks)-1].Time() + 5 + genesis.Config.ShanghaiTime = &time + + n, ethservice := startEthService(t, genesis, preMergeBlocks) + ethservice.Merger().ReachTTD() + defer n.Close() + + api := NewBlockValidationAPI(ethservice, nil, false) + parent := preMergeBlocks[len(preMergeBlocks)-1] + + api.eth.APIBackend.Miner().SetEtherbase(testBuilderAddr) + + statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) + searcherNonce := statedb.GetNonce(testSearcherAddr) + + signer := types.LatestSigner(ethservice.BlockChain().Config()) + + tobTx1, _ := types.SignTx(types.NewTransaction(searcherNonce, common.Address{0x16}, big.NewInt(10), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testSearcherKey) + tobTx2, _ := types.SignTx(types.NewTransaction(searcherNonce+1, common.Address{0x17}, big.NewInt(20), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testSearcherKey) + + tobTx1Bytes, err := tobTx1.MarshalBinary() + require.NoError(t, err) + tobTx2Bytes, err := tobTx2.MarshalBinary() + require.NoError(t, err) + + tobTxs := bellatrixUtil.ExecutionPayloadTransactions{ + Transactions: []bellatrix.Transaction{tobTx1Bytes, tobTx2Bytes}, + } + + withdrawals := []*types.Withdrawal{ + { + Index: 0, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + { + Index: 1, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + } + withdrawalsRoot := types.DeriveSha(types.Withdrawals(withdrawals), trie.NewStackTrie(nil)) + execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{ + Timestamp: parent.Time() + 5, + Withdrawals: withdrawals, + SuggestedFeeRecipient: testValidatorAddr, + }) + fmt.Printf("DEBUG: Exec data random is %x\n", execData.Random) + require.NoError(t, err) + require.EqualValues(t, len(execData.Withdrawals), 2) + require.EqualValues(t, len(execData.Transactions), 0) + + payload, err := ExecutableDataToExecutionPayloadV2(execData) + require.NoError(t, err) + + proposerAddr := bellatrix.ExecutionAddress{} + copy(proposerAddr[:], testValidatorAddr.Bytes()) + + blockRequest := &BuilderBlockValidationRequestV2{ + SubmitBlockRequest: capellaapi.SubmitBlockRequest{ + Signature: phase0.BLSSignature{}, + Message: &apiv1.BidTrace{ + ParentHash: phase0.Hash32(execData.ParentHash), + BlockHash: phase0.Hash32(execData.BlockHash), + ProposerFeeRecipient: proposerAddr, + GasLimit: execData.GasLimit, + GasUsed: execData.GasUsed, + // This value is actual profit + 1, validation should fail + Value: uint256.NewInt(149842511727213), + }, + ExecutionPayload: payload, + }, + RegisteredGasLimit: execData.GasLimit, + WithdrawalsRoot: withdrawalsRoot, + } + + assemblyRequest := BlockAssemblerRequest{ + TobTxs: tobTxs, + RobPayload: blockRequest.SubmitBlockRequest, + RegisteredGasLimit: execData.GasLimit, + } + + block, err := api.BlockAssembler(&assemblyRequest) + require.NoError(t, err) + require.Equal(t, len(block.Transactions), 2) + + // check tob txs + actualTobTx1 := block.Transactions[0] + actualTobTx2 := block.Transactions[1] + require.Equal(t, bellatrixUtil.ExecutionPayloadTransactions{Transactions: []bellatrix.Transaction{actualTobTx1, actualTobTx2}}, tobTxs) +} + +func TestBlockAssemblerWithNoTobTxs(t *testing.T) { + genesis, preMergeBlocks := generatePreMergeChain(20) + os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) + time := preMergeBlocks[len(preMergeBlocks)-1].Time() + 5 + genesis.Config.ShanghaiTime = &time + + n, ethservice := startEthService(t, genesis, preMergeBlocks) + ethservice.Merger().ReachTTD() + defer n.Close() + + api := NewBlockValidationAPI(ethservice, nil, false) + parent := preMergeBlocks[len(preMergeBlocks)-1] + + api.eth.APIBackend.Miner().SetEtherbase(testBuilderAddr) + + statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) + nonce := statedb.GetNonce(testAddr) + + signer := types.LatestSigner(ethservice.BlockChain().Config()) + + robTx1, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x18}, big.NewInt(30), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) + robTx2, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x19}, big.NewInt(40), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) + + ethservice.TxPool().AddLocal(robTx1) + ethservice.TxPool().AddLocal(robTx2) + + withdrawals := []*types.Withdrawal{ + { + Index: 0, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + { + Index: 1, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + } + withdrawalsRoot := types.DeriveSha(types.Withdrawals(withdrawals), trie.NewStackTrie(nil)) + execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{ + Timestamp: parent.Time() + 5, + Withdrawals: withdrawals, + SuggestedFeeRecipient: testValidatorAddr, + }) + fmt.Printf("DEBUG: Exec data random is %x\n", execData.Random) + require.NoError(t, err) + require.EqualValues(t, len(execData.Withdrawals), 2) + require.EqualValues(t, len(execData.Transactions), 2) + + payload, err := ExecutableDataToExecutionPayloadV2(execData) + require.NoError(t, err) + + proposerAddr := bellatrix.ExecutionAddress{} + copy(proposerAddr[:], testValidatorAddr.Bytes()) + + blockRequest := &BuilderBlockValidationRequestV2{ + SubmitBlockRequest: capellaapi.SubmitBlockRequest{ + Signature: phase0.BLSSignature{}, + Message: &apiv1.BidTrace{ + ParentHash: phase0.Hash32(execData.ParentHash), + BlockHash: phase0.Hash32(execData.BlockHash), + ProposerFeeRecipient: proposerAddr, + GasLimit: execData.GasLimit, + GasUsed: execData.GasUsed, + // This value is actual profit + 1, validation should fail + Value: uint256.NewInt(149842511727213), + }, + ExecutionPayload: payload, + }, + RegisteredGasLimit: execData.GasLimit, + WithdrawalsRoot: withdrawalsRoot, + } + + assemblyRequest := BlockAssemblerRequest{ + TobTxs: bellatrixUtil.ExecutionPayloadTransactions{ + Transactions: []bellatrix.Transaction{}, + }, + RobPayload: blockRequest.SubmitBlockRequest, + RegisteredGasLimit: execData.GasLimit, + } + + block, err := api.BlockAssembler(&assemblyRequest) + require.NoError(t, err) + require.Equal(t, len(block.Transactions), 2) + + // check rob txs + actualRobTx1 := block.Transactions[0] + actualRobTx2 := block.Transactions[1] + execDataTx1 := bellatrix.Transaction(execData.Transactions[0]) + execDataTx2 := bellatrix.Transaction(execData.Transactions[1]) + + require.Equal(t, execDataTx1, actualRobTx1) + require.Equal(t, execDataTx2, actualRobTx2) +} + +func TestBlockAssemblerWithTobAndRobTxs(t *testing.T) { + genesis, preMergeBlocks := generatePreMergeChain(20) + os.Setenv("BUILDER_TX_SIGNING_KEY", testBuilderKeyHex) + time := preMergeBlocks[len(preMergeBlocks)-1].Time() + 5 + genesis.Config.ShanghaiTime = &time + + n, ethservice := startEthService(t, genesis, preMergeBlocks) + ethservice.Merger().ReachTTD() + defer n.Close() + + api := NewBlockValidationAPI(ethservice, nil, false) + parent := preMergeBlocks[len(preMergeBlocks)-1] + + api.eth.APIBackend.Miner().SetEtherbase(testBuilderAddr) + + statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) + nonce := statedb.GetNonce(testAddr) + searcherNonce := statedb.GetNonce(testSearcherAddr) + + signer := types.LatestSigner(ethservice.BlockChain().Config()) + + tobTx1, _ := types.SignTx(types.NewTransaction(searcherNonce, common.Address{0x16}, big.NewInt(10), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testSearcherKey) + tobTx2, _ := types.SignTx(types.NewTransaction(searcherNonce+1, common.Address{0x17}, big.NewInt(20), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testSearcherKey) + + tobTx1Bytes, err := tobTx1.MarshalBinary() + require.NoError(t, err) + tobTx2Bytes, err := tobTx2.MarshalBinary() + require.NoError(t, err) + + tobTxs := bellatrixUtil.ExecutionPayloadTransactions{ + Transactions: []bellatrix.Transaction{tobTx1Bytes, tobTx2Bytes}, + } + + robTx1, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x18}, big.NewInt(30), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) + robTx2, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x19}, big.NewInt(40), 21000, big.NewInt(2*params.InitialBaseFee), nil), signer, testKey) + + ethservice.TxPool().AddLocal(robTx1) + ethservice.TxPool().AddLocal(robTx2) + + withdrawals := []*types.Withdrawal{ + { + Index: 0, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + { + Index: 1, + Validator: 1, + Amount: 100, + Address: testAddr, + }, + } + withdrawalsRoot := types.DeriveSha(types.Withdrawals(withdrawals), trie.NewStackTrie(nil)) + execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{ + Timestamp: parent.Time() + 5, + Withdrawals: withdrawals, + SuggestedFeeRecipient: testValidatorAddr, + }) + fmt.Printf("DEBUG: Exec data random is %x\n", execData.Random) + require.NoError(t, err) + require.EqualValues(t, len(execData.Withdrawals), 2) + require.EqualValues(t, len(execData.Transactions), 2) + + payload, err := ExecutableDataToExecutionPayloadV2(execData) + require.NoError(t, err) + + proposerAddr := bellatrix.ExecutionAddress{} + copy(proposerAddr[:], testValidatorAddr.Bytes()) + + blockRequest := &BuilderBlockValidationRequestV2{ + SubmitBlockRequest: capellaapi.SubmitBlockRequest{ + Signature: phase0.BLSSignature{}, + Message: &apiv1.BidTrace{ + ParentHash: phase0.Hash32(execData.ParentHash), + BlockHash: phase0.Hash32(execData.BlockHash), + ProposerFeeRecipient: proposerAddr, + GasLimit: execData.GasLimit, + GasUsed: execData.GasUsed, + // This value is actual profit + 1, validation should fail + Value: uint256.NewInt(149842511727213), + }, + ExecutionPayload: payload, + }, + RegisteredGasLimit: execData.GasLimit, + WithdrawalsRoot: withdrawalsRoot, + } + + assemblyRequest := BlockAssemblerRequest{ + TobTxs: tobTxs, + RobPayload: blockRequest.SubmitBlockRequest, + RegisteredGasLimit: execData.GasLimit, + } + + block, err := api.BlockAssembler(&assemblyRequest) + require.NoError(t, err) + require.Equal(t, len(block.Transactions), 4) + + // check tob txs + actualTobTx1 := block.Transactions[0] + actualTobTx2 := block.Transactions[1] + require.Equal(t, bellatrixUtil.ExecutionPayloadTransactions{Transactions: []bellatrix.Transaction{actualTobTx1, actualTobTx2}}, tobTxs) + + // check rob txs + actualRobTx1 := block.Transactions[2] + actualRobTx2 := block.Transactions[3] + execDataTx1 := bellatrix.Transaction(execData.Transactions[0]) + execDataTx2 := bellatrix.Transaction(execData.Transactions[1]) + + require.Equal(t, execDataTx1, actualRobTx1) + require.Equal(t, execDataTx2, actualRobTx2) +} func TestValidateBuilderSubmissionV2(t *testing.T) { genesis, preMergeBlocks := generatePreMergeChain(20) @@ -347,7 +627,7 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { config := params.AllEthashProtocolChanges genesis := &core.Genesis{ Config: config, - Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}, testValidatorAddr: {Balance: testBalance}, testBuilderAddr: {Balance: testBalance}}, + Alloc: core.GenesisAlloc{testSearcherAddr: {Balance: testBalance}, testAddr: {Balance: testBalance}, testValidatorAddr: {Balance: testBalance}, testBuilderAddr: {Balance: testBalance}}, ExtraData: []byte("test genesis"), Timestamp: 9000, BaseFee: big.NewInt(params.InitialBaseFee), diff --git a/miner/multi_worker.go b/miner/multi_worker.go index fdc225d45c..d0d11848ed 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -118,19 +118,17 @@ func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) var tobBlock *types.Block var fees *big.Int - for _, worker := range w.workers { - var err error - start := time.Now() - tobBlock, fees, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) - if err != nil { - log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) - continue - } - log.Info("DEBUG: Got assembler payload!!\n") - log.Info("DEBUG: assembler Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") - payload.update(tobBlock, fees, time.Since(start)) - break + var err error + start := time.Now() + log.Info("DEBUG: Get assembled sealing block!") + tobBlock, fees, err = w.regularWorker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) + if err != nil { + log.Error("could not start async block construction", "err", err) + return nil, err } + log.Info("DEBUG: Got assembler payload!!\n") + log.Info("DEBUG: assembler Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") + payload.update(tobBlock, fees, time.Since(start)) return payload, nil } diff --git a/miner/worker.go b/miner/worker.go index 4b0376a323..219f4d14c1 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -906,6 +906,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co profit: new(big.Int), assemblerTxs: assemblerTxs, } + log.Info("DEBUG: assemblerTxs in makeEnv", "assemblerTxs", assemblerTxs) if len(assemblerTxs.TobTxs) > 0 || (assemblerTxs.RobTxs != nil && assemblerTxs.RobTxs.Len() > 0) { env.isAssembler = true } @@ -1109,7 +1110,9 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem var coalescedLogs []*types.Log // first go thru TOB txs - for _, tx := range assemblerTxs.TobTxs { + log.Info("DEBUG: Commiting TOB txs!!") + for i, tx := range assemblerTxs.TobTxs { + log.Info("DEBUG: Commiting TOB tx ", "number", i) // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1126,6 +1129,7 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) logs, err := w.commitTransaction(env, tx) + log.Info("DEBUG: Tob tx commit failed with err", "err", err) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1158,6 +1162,7 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // commit the ROB txs i := 0 for { + log.Info("DEBUG: Commiting ROB tx ", "number", i) // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1180,6 +1185,7 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) logs, err := w.commitTransaction(env, tx) + log.Info("DEBUG: ROB tx commit failed with err", "err", err) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1335,7 +1341,6 @@ type generateParams struct { noUncle bool // Flag whether the uncle block inclusion is allowed noTxs bool // Flag whether an empty block without any transaction is expected onBlock BlockHookFn // Callback to call for each produced block - isAssembler bool // Are we generating a block as an assembler assemblerTxs AssemblerTxLists // The transactions that the assembler wants to make a block out of } @@ -1489,6 +1494,8 @@ func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ( i += 1 } + log.Info("DEBUG: mempoolHashes length is ", "len", len(mempoolHashes)) + log.Info("DEBUG: mempoolHashes is ", "mempoolHashes", mempoolHashes) if totalTxsToAssemble > 0 { log.Info("DEBUG: Commiting assembler txs!!!\n") @@ -1709,7 +1716,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e gasUsedGauge.Update(int64(block.GasUsed())) transactionNumGauge.Update(int64(len(env.txs))) } - if !params.isAssembler { + if !work.isAssembler { if params.onBlock != nil { log.Info("DEBUG: Running ROB block hook!!\n") go params.onBlock(block, profit, orderCloseTime, blockBundles, allBundles, usedSbundles) @@ -1723,9 +1730,10 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, time.Now(), nil, nil, nil, true) } + log.Info("DEBUG: is assembler", "isAssembler", work.isAssembler) var paymentTxReserve *proposerTxReservation // there won't be any additional payments for a block assembled by the assembler - if !params.isAssembler { + if !work.isAssembler { paymentTxReserve, err = w.proposerTxPrepare(work, &validatorCoinbase) if err != nil { return nil, nil, err @@ -1766,7 +1774,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e // log out no of bundles log.Info("DEBUG: Got bundles from the mempool!", "bundles", len(blockBundles)) - if !params.isAssembler { + if !work.isAssembler { err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) if err != nil { return nil, nil, err From 32ce5368de8d3b0cec5fd1ccec0fc3c46cae9b74 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Mon, 11 Sep 2023 20:17:17 +0530 Subject: [PATCH 09/19] add TODO --- miner/worker.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/miner/worker.go b/miner/worker.go index 219f4d14c1..39c126236b 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1759,6 +1759,8 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e } } + // TODO - bchain - maybe we can avoid this check for the assembler since we can assume that the ROB block + // should be valid err = VerifyBundlesAtomicity(work, blockBundles, allBundles, usedSbundles, mempoolTxHashes) if err != nil { log.Error("Bundle invariant is violated for built block", "block", work.header.Number, "err", err) From e6ab4ef3b5eebbaa2465f83adad462334e0b53c9 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 14:32:57 +0530 Subject: [PATCH 10/19] add pepc-relayer endpoint --- builder/builder.go | 18 ++++++++++------ builder/local_relay.go | 9 ++++---- builder/relay.go | 35 ++++++++++++++++---------------- builder/relay_aggregator.go | 7 +++---- builder/relay_aggregator_test.go | 15 ++++---------- core/types/builder.go | 4 +++- 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index cf0541de5c..58b0a010c1 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -47,7 +47,8 @@ type ValidatorData struct { type IRelay interface { SubmitBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error - SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error + // this is for the builder to be aware of pepc boost specific features + IsPepcRelayer() (bool, error) SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) @@ -204,10 +205,10 @@ func (b *Builder) Stop() error { func (b *Builder) onSealedBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isRobBlock bool) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { if b.eth.Config().IsShanghai(block.Time()) { log.Info("DEBUG: Submitting capella block\n") - if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs, isRobBlock); err != nil { + if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs); err != nil { return err } } else { @@ -283,7 +284,7 @@ func (b *Builder) submitBellatrixBlock(block *types.Block, blockValue *big.Int, func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, ordersClosedAt, sealedAt time.Time, commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, - proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes, isRobBlock bool) error { + proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { executableData := engine.BlockToExecutableData(block, blockValue) payload, err := executableDataToCapellaExecutionPayload(executableData.ExecutionPayload) if err != nil { @@ -328,7 +329,7 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or } } else { go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg) - if isRobBlock { + if attrs.IsPepcRelayer { log.Info("DEBUG: submitting rob capella block") err = b.relay.SubmitRobBlockCapella(&blockSubmitReq, vd) if err != nil { @@ -358,10 +359,15 @@ func (b *Builder) OnPayloadAttribute(attrs *types.BuilderPayloadAttributes) erro if err != nil { return fmt.Errorf("could not get validator while submitting block for slot %d - %w", attrs.Slot, err) } + isPepcRelayer, err := b.relay.IsPepcRelayer() + if err != nil { + return fmt.Errorf("could not get pepc relayer status - %w", err) + } log.Info("DEBUG: validator info is", "vd", vd) attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) attrs.GasLimit = vd.GasLimit + attrs.IsPepcRelayer = isPepcRelayer proposerPubkey, err := utils.HexToPubkey(string(vd.Pubkey)) if err != nil { @@ -435,7 +441,7 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. queueMu.Lock() if queueBestEntry.block.Hash() != queueLastSubmittedHash { err := b.onSealedBlock(queueBestEntry.block, queueBestEntry.blockValue, queueBestEntry.ordersCloseTime, queueBestEntry.sealedAt, - queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs, true) + queueBestEntry.commitedBundles, queueBestEntry.allBundles, queueBestEntry.usedSbundles, proposerPubkey, vd, attrs) if err != nil { log.Error("could not run sealed block hook", "err", err) diff --git a/builder/local_relay.go b/builder/local_relay.go index 21c12c3846..8facb516ed 100644 --- a/builder/local_relay.go +++ b/builder/local_relay.go @@ -66,11 +66,6 @@ type LocalRelay struct { fd ForkData } -func (r *LocalRelay) SubmitRobBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func (r *LocalRelay) SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me panic("implement me") @@ -125,6 +120,10 @@ func (r *LocalRelay) SubmitBlock(msg *bellatrixapi.SubmitBlockRequest, _ Validat return r.submitBlock(msg) } +func (r *LocalRelay) IsPepcRelayer() (bool, error) { + return true, nil +} + func (r *LocalRelay) SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, _ ValidatorData) error { log.Info("submitting block to local relay", "block", msg.ExecutionPayload.BlockHash.String()) diff --git a/builder/relay.go b/builder/relay.go index d794276827..3e04d24004 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -157,24 +157,6 @@ func (r *RemoteRelay) SubmitBlock(msg *bellatrix.SubmitBlockRequest, _ Validator return nil } -func (r *RemoteRelay) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, _ ValidatorData) error { - log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) - endpoint := r.config.Endpoint + "/relay/v1/builder/rob_blocks" - code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - - if r.localRelay != nil { - r.localRelay.submitBlock(msg) - } - - return nil -} - func (r *RemoteRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { log.Info("submitting block to remote relay", "endpoint", r.config.Endpoint) @@ -248,6 +230,23 @@ func (r *RemoteRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, _ V return nil } +func (r *RemoteRelay) IsPepcRelayer() (bool, error) { + log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) + + endpoint := r.config.Endpoint + "/relay/v1/data/is_pepc_relayer" + + var isPepcRelayer bool + code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, nil, isPepcRelayer) + if err != nil { + return false, fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) + } + if code > 299 { + return false, fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) + } + + return isPepcRelayer, nil +} + func (r *RemoteRelay) getSlotValidatorMapFromRelay() (map[uint64]ValidatorData, error) { var dst GetValidatorRelayResponse code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, r.config.Endpoint+"/relay/v1/builder/validators", nil, &dst) diff --git a/builder/relay_aggregator.go b/builder/relay_aggregator.go index 57f4d43ed8..67ec9ead44 100644 --- a/builder/relay_aggregator.go +++ b/builder/relay_aggregator.go @@ -18,14 +18,13 @@ type RemoteRelayAggregator struct { registrationsCache map[ValidatorData][]IRelay } -func (r *RemoteRelayAggregator) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") +func (r *RemoteRelayAggregator) IsPepcRelayer() (bool, error) { + return false, fmt.Errorf("not implemented") } func (r *RemoteRelayAggregator) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { //TODO implement me - panic("implement me") + return fmt.Errorf("not implemented") } func NewRemoteRelayAggregator(primary IRelay, secondary []IRelay) *RemoteRelayAggregator { diff --git a/builder/relay_aggregator_test.go b/builder/relay_aggregator_test.go index 1d7de6f882..cce1483d0e 100644 --- a/builder/relay_aggregator_test.go +++ b/builder/relay_aggregator_test.go @@ -57,17 +57,6 @@ func (r *testRelay) SubmitBlock(msg *bellatrix.SubmitBlockRequest, registration return r.sbError } -func (r *testRelay) SubmitRobBlock(msg *bellatrix.SubmitBlockRequest, registration ValidatorData) error { - if r.submittedMsgCh != nil { - select { - case r.submittedMsgCh <- msg: - default: - } - } - r.submittedMsg = msg - return r.sbError -} - func (r *testRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, registration ValidatorData) error { if r.submittedMsgCh != nil { select { @@ -95,6 +84,10 @@ func (r *testRelay) GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) return r.gvsVd, r.gvsErr } +func (r *testRelay) IsPepcRelayer() (bool, error) { + return false, nil +} + func (r *testRelay) Start() error { return nil } diff --git a/core/types/builder.go b/core/types/builder.go index d31da82fdb..735040d4d4 100644 --- a/core/types/builder.go +++ b/core/types/builder.go @@ -14,6 +14,7 @@ type BuilderPayloadAttributes struct { HeadHash common.Hash `json:"blockHash"` Withdrawals Withdrawals `json:"withdrawals"` GasLimit uint64 + IsPepcRelayer bool `json:"isPepcRelayer"` } func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bool { @@ -22,7 +23,8 @@ func (attrs *BuilderPayloadAttributes) Equal(other *BuilderPayloadAttributes) bo attrs.SuggestedFeeRecipient != other.SuggestedFeeRecipient || attrs.Slot != other.Slot || attrs.HeadHash != other.HeadHash || - attrs.GasLimit != other.GasLimit { + attrs.GasLimit != other.GasLimit || + attrs.IsPepcRelayer != other.IsPepcRelayer { return false } From fd8826e9cabc1f1ab1cba6b011524bf367186805 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 16:54:23 +0530 Subject: [PATCH 11/19] avoid sending to ROB endpoint --- builder/builder.go | 27 ++++++------------------ builder/local_relay.go | 5 ----- builder/relay.go | 35 -------------------------------- builder/relay_aggregator.go | 5 ----- builder/relay_aggregator_test.go | 11 ---------- eth/block-validation/api.go | 4 +++- 6 files changed, 9 insertions(+), 78 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 58b0a010c1..2413eaa753 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -50,7 +50,6 @@ type IRelay interface { // this is for the builder to be aware of pepc boost specific features IsPepcRelayer() (bool, error) SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error - SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) Config() RelayConfig Start() error @@ -329,20 +328,11 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or } } else { go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg) - if attrs.IsPepcRelayer { - log.Info("DEBUG: submitting rob capella block") - err = b.relay.SubmitRobBlockCapella(&blockSubmitReq, vd) - if err != nil { - log.Error("could not submit rob capella block", "err", err, "#commitedBundles", len(commitedBundles)) - return err - } - } else { - log.Info("DEBUG: Submitting regular FULL_BLOCK capella block") - err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) - if err != nil { - log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) - return err - } + log.Info("DEBUG: Submitting regular FULL_BLOCK capella block") + err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) + if err != nil { + log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) + return err } } @@ -359,15 +349,10 @@ func (b *Builder) OnPayloadAttribute(attrs *types.BuilderPayloadAttributes) erro if err != nil { return fmt.Errorf("could not get validator while submitting block for slot %d - %w", attrs.Slot, err) } - isPepcRelayer, err := b.relay.IsPepcRelayer() - if err != nil { - return fmt.Errorf("could not get pepc relayer status - %w", err) - } - log.Info("DEBUG: validator info is", "vd", vd) attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) attrs.GasLimit = vd.GasLimit - attrs.IsPepcRelayer = isPepcRelayer + attrs.IsPepcRelayer = true proposerPubkey, err := utils.HexToPubkey(string(vd.Pubkey)) if err != nil { diff --git a/builder/local_relay.go b/builder/local_relay.go index 8facb516ed..26e3fbdc7c 100644 --- a/builder/local_relay.go +++ b/builder/local_relay.go @@ -66,11 +66,6 @@ type LocalRelay struct { fd ForkData } -func (r *LocalRelay) SubmitRobBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - panic("implement me") -} - func NewLocalRelay(sk *bls.SecretKey, beaconClient IBeaconClient, builderSigningDomain, proposerSigningDomain phase0.Domain, fd ForkData, enableBeaconChecks bool) (*LocalRelay, error) { blsPk, err := bls.PublicKeyFromSecretKey(sk) if err != nil { diff --git a/builder/relay.go b/builder/relay.go index 3e04d24004..a2d58f91cc 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -195,41 +195,6 @@ func (r *RemoteRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, _ Vali return nil } -func (r *RemoteRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, _ ValidatorData) error { - log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) - - endpoint := r.config.Endpoint + "/relay/v1/builder/rob_blocks" - - if r.config.SszEnabled { - bodyBytes, err := msg.MarshalSSZ() - if err != nil { - return fmt.Errorf("error marshaling ssz: %w", err) - } - log.Debug("submitting block to remote relay", "endpoint", r.config.Endpoint) - code, err := SendSSZRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, bodyBytes, r.config.GzipEnabled) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - } else { - code, err := SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, endpoint, msg, nil) - if err != nil { - return fmt.Errorf("error sending http request to relay %s. err: %w", r.config.Endpoint, err) - } - if code > 299 { - return fmt.Errorf("non-ok response code %d from relay %s", code, r.config.Endpoint) - } - } - - if r.localRelay != nil { - r.localRelay.submitBlockCapella(msg) - } - - return nil -} - func (r *RemoteRelay) IsPepcRelayer() (bool, error) { log.Info("submitting rob block to remote relay", "endpoint", r.config.Endpoint) diff --git a/builder/relay_aggregator.go b/builder/relay_aggregator.go index 67ec9ead44..b15c989f21 100644 --- a/builder/relay_aggregator.go +++ b/builder/relay_aggregator.go @@ -22,11 +22,6 @@ func (r *RemoteRelayAggregator) IsPepcRelayer() (bool, error) { return false, fmt.Errorf("not implemented") } -func (r *RemoteRelayAggregator) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, vd ValidatorData) error { - //TODO implement me - return fmt.Errorf("not implemented") -} - func NewRemoteRelayAggregator(primary IRelay, secondary []IRelay) *RemoteRelayAggregator { relays := []IRelay{primary} return &RemoteRelayAggregator{ diff --git a/builder/relay_aggregator_test.go b/builder/relay_aggregator_test.go index cce1483d0e..ab902cc028 100644 --- a/builder/relay_aggregator_test.go +++ b/builder/relay_aggregator_test.go @@ -68,17 +68,6 @@ func (r *testRelay) SubmitBlockCapella(msg *capella.SubmitBlockRequest, registra return r.sbError } -func (r *testRelay) SubmitRobBlockCapella(msg *capella.SubmitBlockRequest, registration ValidatorData) error { - if r.submittedMsgCh != nil { - select { - case r.submittedMsgChCapella <- msg: - default: - } - } - r.submittedMsgCapella = msg - return r.sbError -} - func (r *testRelay) GetValidatorForSlot(nextSlot uint64) (ValidatorData, error) { r.requestedSlot = nextSlot return r.gvsVd, r.gvsErr diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 404cade0d2..896f3dab8a 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -323,6 +323,8 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c // TODO - check for gas limits // TODO - support for payouts + // TODO - if there are no TOB txs then we can just simulate the block rather then re-assembling it. + // check if there are any duplicate txs // we can error out if there is a nonce gap // TODO - don't error out, but drop the duplicate tx in the ROB block @@ -342,7 +344,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c } seenTxMap[tx.Hash()] = struct{}{} } - log.Info("Done Checking for duplicate txs!") + log.Info("DEBUG: Done Checking for duplicate txs!") withdrawals := make(types.Withdrawals, len(params.RobPayload.ExecutionPayload.Withdrawals)) log.Info("DEBUG: Building the final set of withdrawals") From b3837f8ff2ddbb76c06740cc34f44512c5a30513 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:22:13 +0530 Subject: [PATCH 12/19] remove logs --- builder/builder.go | 6 ------ builder/eth_service.go | 3 --- builder/relay.go | 3 --- eth/block-validation/api.go | 15 --------------- miner/miner.go | 2 -- miner/multi_worker.go | 7 ------- miner/worker.go | 23 ----------------------- 7 files changed, 59 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 2413eaa753..8f60e8d1c1 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -206,7 +206,6 @@ func (b *Builder) onSealedBlock(block *types.Block, blockValue *big.Int, ordersC commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { if b.eth.Config().IsShanghai(block.Time()) { - log.Info("DEBUG: Submitting capella block\n") if err := b.submitCapellaBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, proposerPubkey, vd, attrs); err != nil { return err } @@ -328,7 +327,6 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or } } else { go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg) - log.Info("DEBUG: Submitting regular FULL_BLOCK capella block") err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd) if err != nil { log.Error("could not submit capella block", "err", err, "#commitedBundles", len(commitedBundles)) @@ -349,7 +347,6 @@ func (b *Builder) OnPayloadAttribute(attrs *types.BuilderPayloadAttributes) erro if err != nil { return fmt.Errorf("could not get validator while submitting block for slot %d - %w", attrs.Slot, err) } - log.Info("DEBUG: validator info is", "vd", vd) attrs.SuggestedFeeRecipient = [20]byte(vd.FeeRecipient) attrs.GasLimit = vd.GasLimit attrs.IsPepcRelayer = true @@ -421,8 +418,6 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. log.Debug("runBuildingJob", "slot", attrs.Slot, "parent", attrs.HeadHash, "payloadTimestamp", uint64(attrs.Timestamp), "gasLimit", attrs.GasLimit) submitBestBlock := func() { - log.Info("DEBUG: In submit best block!") - log.Info("DEBUG: Submitting ROB block!!", "isRobBlock", true) queueMu.Lock() if queueBestEntry.block.Hash() != queueLastSubmittedHash { err := b.onSealedBlock(queueBestEntry.block, queueBestEntry.blockValue, queueBestEntry.ordersCloseTime, queueBestEntry.sealedAt, @@ -453,7 +448,6 @@ func (b *Builder) runBuildingJob(slotCtx context.Context, proposerPubkey phase0. return } - log.Info("DEBUG: Running ROB block hook\n") sealedAt := time.Now() queueMu.Lock() diff --git a/builder/eth_service.go b/builder/eth_service.go index 349de61a04..26e3cf9379 100644 --- a/builder/eth_service.go +++ b/builder/eth_service.go @@ -54,7 +54,6 @@ func NewEthereumService(eth *eth.Ethereum) *EthereumService { func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error { // Send a request to generate a full block in the background. // The result can be obtained via the returned channel. - log.Info("DEBUG: In build block!!") args := &miner.BuildPayloadArgs{ Parent: attrs.HeadHash, Timestamp: uint64(attrs.Timestamp), @@ -64,9 +63,7 @@ func (s *EthereumService) BuildBlock(attrs *types.BuilderPayloadAttributes, seal Withdrawals: attrs.Withdrawals, BlockHook: sealedBlockCallback, } - log.Info("DEBUG: Args for build block!!", "args", args) - log.Info("DEBUG: Starting to build ROB payload!!") payload, err := s.eth.Miner().BuildPayload(args) if err != nil { log.Error("Failed to build payload", "err", err) diff --git a/builder/relay.go b/builder/relay.go index a2d58f91cc..b8b87789ac 100644 --- a/builder/relay.go +++ b/builder/relay.go @@ -71,7 +71,6 @@ func (r *RemoteRelay) updateValidatorsMap(currentSlot uint64, retries int) error r.validatorSyncOngoing = true r.validatorsLock.Unlock() - //log.Info("DEBUG: requesting from relayer", "currentSlot", currentSlot) newMap, err := r.getSlotValidatorMapFromRelay() for err != nil && retries > 0 { log.Error("could not get validators map from relay, retrying", "err", err) @@ -99,12 +98,10 @@ func (r *RemoteRelay) GetValidatorForSlot(nextSlot uint64) (ValidatorData, error // next slot is expected to be the actual chain's next slot, not something requested by the user! // if not sanitized it will force resync of validator data and possibly is a DoS vector - //log.Info("DEBUG: Getting validator for slot", "slot", nextSlot) r.validatorsLock.RLock() if r.lastRequestedSlot == 0 || nextSlot/32 > r.lastRequestedSlot/32 { // Every epoch request validators map go func() { - //log.Info("DEBUG: Updating validators map", "slot", nextSlot) err := r.updateValidatorsMap(nextSlot, 1) if err != nil { log.Error("could not update validators map", "err", err) diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 896f3dab8a..8fa1f6a9f2 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -304,7 +304,6 @@ func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*cape } func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*capella.ExecutionPayload, error) { - log.Info("DEBUG: Entered the BlockAssembler!!") log.Info("BlockAssembler", "tobTxs", len(params.TobTxs.Transactions), "robPayload", params.RobPayload) transactionBytes := make([][]byte, len(params.TobTxs.Transactions)) for i, txHexBytes := range params.TobTxs.Transactions { @@ -328,7 +327,6 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c // check if there are any duplicate txs // we can error out if there is a nonce gap // TODO - don't error out, but drop the duplicate tx in the ROB block - log.Info("DEBUG: Checking for duplicate txs") seenTxMap := make(map[common.Hash]struct{}) for _, tx := range txs { // If we see nonce reuse in TOB then fail @@ -344,11 +342,8 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c } seenTxMap[tx.Hash()] = struct{}{} } - log.Info("DEBUG: Done Checking for duplicate txs!") withdrawals := make(types.Withdrawals, len(params.RobPayload.ExecutionPayload.Withdrawals)) - log.Info("DEBUG: Building the final set of withdrawals") - log.Info("DEBUG: Withdrawals are ", "withdrawals", params.RobPayload.ExecutionPayload.Withdrawals) for i, withdrawal := range params.RobPayload.ExecutionPayload.Withdrawals { withdrawals[i] = &types.Withdrawal{ Index: uint64(withdrawal.Index), @@ -358,14 +353,9 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c } } - log.Info("DEBUG: Built the final set of withdrawals") - log.Info("DEBUG: Withdrawals are ", "withdrawals", withdrawals) // assemble the txs in map[sender]txs format and pass it in the BuildPayload call robTxs := robBlock.Transactions() - log.Info("DEBUG: Rob txs are ", "robTxs", robTxs) - log.Info("DEBUG: Tob txs are ", "tobTxs", txs) - log.Info("DEBUG: Assembling the block!!") block, err := api.eth.Miner().PayloadAssembler(&miner.BuildPayloadArgs{ Parent: common.Hash(params.RobPayload.ExecutionPayload.ParentHash), Timestamp: params.RobPayload.ExecutionPayload.Timestamp, @@ -380,11 +370,9 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c RobTxs: &robTxs, }, }) - log.Info("DEBUG: Assembled the block!!") if err != nil { return nil, err } - log.Info("DEBUG: Resolving block!!") resolvedBlock := block.ResolveFull() if resolvedBlock == nil { return nil, errors.New("unable to resolve block") @@ -393,11 +381,8 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c return nil, errors.New("nil execution payload") } - log.Info("DEBUG: Resolved block!!") finalPayload, err := executableDataToCapellaExecutionPayload(resolvedBlock.ExecutionPayload) - log.Info("DEBUG: Final payload", "finalPayload", finalPayload) - log.Info("DEBUG: Returning the final payload!!") return finalPayload, nil } diff --git a/miner/miner.go b/miner/miner.go index df8201cdf7..ecd5c0855b 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -308,8 +308,6 @@ func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscript // Accepts the block, time at which orders were taken, bundles which were used to build the block and all bundles that were considered for the block type BlockHookFn = func(*types.Block, *big.Int, time.Time, []types.SimulatedBundle, []types.SimulatedBundle, []types.UsedSBundle) -type TobBlockHookFn = func(*types.Block, *big.Int) - // BuildPayload builds the payload according to the provided parameters. func (miner *Miner) PayloadAssembler(args *BuildPayloadArgs) (*Payload, error) { return miner.worker.payloadAssembler(args) diff --git a/miner/multi_worker.go b/miner/multi_worker.go index d0d11848ed..d528f5a8bf 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -88,7 +88,6 @@ func (w *multiWorker) disablePreseal() { } func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) { - log.Info("DEBUG: Starting to assemble payload!!!!\n") // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. @@ -107,8 +106,6 @@ func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) return nil, errors.New("no worker could build an empty block") } - log.Info("DEBUG: Got empty assembler payload!!\n") - // Construct a payload object for return. payload := newPayload(empty, args.Id()) @@ -120,21 +117,17 @@ func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) var fees *big.Int var err error start := time.Now() - log.Info("DEBUG: Get assembled sealing block!") tobBlock, fees, err = w.regularWorker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) if err != nil { log.Error("could not start async block construction", "err", err) return nil, err } - log.Info("DEBUG: Got assembler payload!!\n") - log.Info("DEBUG: assembler Payload details", "tobBlock", tobBlock, "fees", fees, "time", time.Since(start), "\n") payload.update(tobBlock, fees, time.Since(start)) return payload, nil } func (w *multiWorker) buildPayload(args *BuildPayloadArgs) (*Payload, error) { - log.Info("DEBUG: In ROB payload building!!\n") // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. diff --git a/miner/worker.go b/miner/worker.go index 39c126236b..962fb8a1cd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -906,7 +906,6 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co profit: new(big.Int), assemblerTxs: assemblerTxs, } - log.Info("DEBUG: assemblerTxs in makeEnv", "assemblerTxs", assemblerTxs) if len(assemblerTxs.TobTxs) > 0 || (assemblerTxs.RobTxs != nil && assemblerTxs.RobTxs.Len() > 0) { env.isAssembler = true } @@ -1110,9 +1109,7 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem var coalescedLogs []*types.Log // first go thru TOB txs - log.Info("DEBUG: Commiting TOB txs!!") for i, tx := range assemblerTxs.TobTxs { - log.Info("DEBUG: Commiting TOB tx ", "number", i) // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1129,7 +1126,6 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) logs, err := w.commitTransaction(env, tx) - log.Info("DEBUG: Tob tx commit failed with err", "err", err) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1162,7 +1158,6 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // commit the ROB txs i := 0 for { - log.Info("DEBUG: Commiting ROB tx ", "number", i) // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1185,7 +1180,6 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) logs, err := w.commitTransaction(env, tx) - log.Info("DEBUG: ROB tx commit failed with err", "err", err) switch { case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -1453,11 +1447,9 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) err error ) if env.isAssembler { - log.Info("DEBUG: Actually assembling the txs!!\n") blockBundles, allBundles, mempoolTxHashes, err = w.fillAssemblerTransactions(interrupt, env) return blockBundles, allBundles, usedSbundles, mempoolTxHashes, err } - log.Info("DEBUG: Filling up ROB txs!!!\n") switch w.flashbots.algoType { case ALGO_GREEDY, ALGO_GREEDY_BUCKETS: blockBundles, allBundles, usedSbundles, mempoolTxHashes, err = w.fillTransactionsAlgoWorker(interrupt, env) @@ -1474,7 +1466,6 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) // be customized with the plugin in the future. // Returns error if any, otherwise the bundles that made it into the block and all bundles that passed simulation func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ([]types.SimulatedBundle, []types.SimulatedBundle, map[common.Hash]struct{}, error) { - log.Info("DEBUG: In fillAssemblerTransactions!!!\n") assemblerTxs := env.assemblerTxs totalTxsToAssemble := assemblerTxs.RobTxs.Len() + len(assemblerTxs.TobTxs) @@ -1494,18 +1485,13 @@ func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ( i += 1 } - log.Info("DEBUG: mempoolHashes length is ", "len", len(mempoolHashes)) - log.Info("DEBUG: mempoolHashes is ", "mempoolHashes", mempoolHashes) if totalTxsToAssemble > 0 { - log.Info("DEBUG: Commiting assembler txs!!!\n") if err := w.commitAssemblyTransactions(env, env.assemblerTxs, interrupt); err != nil { return nil, nil, nil, err } } - log.Info("DEBUG: Assembler environment is ", "env", env) - return []types.SimulatedBundle{}, []types.SimulatedBundle{}, mempoolHashes, nil } @@ -1718,7 +1704,6 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e } if !work.isAssembler { if params.onBlock != nil { - log.Info("DEBUG: Running ROB block hook!!\n") go params.onBlock(block, profit, orderCloseTime, blockBundles, allBundles, usedSbundles) } } @@ -1730,7 +1715,6 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e return finalizeFn(work, time.Now(), nil, nil, nil, true) } - log.Info("DEBUG: is assembler", "isAssembler", work.isAssembler) var paymentTxReserve *proposerTxReservation // there won't be any additional payments for a block assembled by the assembler if !work.isAssembler { @@ -1769,13 +1753,9 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e // no bundles or tx from mempool if len(work.txs) == 0 { - log.Info("DEBUG: Got no txs from the mempool!") return finalizeFn(work, orderCloseTime, blockBundles, allBundles, usedSbundles, true) } - // log out no of bundles - log.Info("DEBUG: Got bundles from the mempool!", "bundles", len(blockBundles)) - if !work.isAssembler { err = w.proposerTxCommit(work, &validatorCoinbase, paymentTxReserve) if err != nil { @@ -2365,9 +2345,6 @@ func (w *worker) proposerTxCommit(env *environment, validatorCoinbase *common.Ad w.mu.Unlock() builderBalance := env.state.GetBalance(sender) - log.Info("DEBUG: builder current balance is \n", "builderBalance", builderBalance) - log.Info("DEBUG: builder reserved balance is \n", "reserveBalance", reserve.builderBalance) - availableFunds := new(big.Int).Sub(builderBalance, reserve.builderBalance) if availableFunds.Sign() <= 0 { return errors.New("builder balance decreased") From 9ea32b00de9de9a0bcca52233b0f7cccc5217f8c Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:23:44 +0530 Subject: [PATCH 13/19] remove commented out code --- core/types/transaction.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 206041760f..eda68bbb3b 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -599,15 +599,6 @@ func NewSBundleWithMinerFee(sbundle *SimSBundle, _ *big.Int) (*TxWithMinerFee, e }, nil } -// Used only to assemble txs. During tx assembly, we need to have txs in the sequence specified by assembly request -//type AssemblerTxList []*TxWithMinerFee -// -//func (s AssemblerTxList) Len() int { return len(s) } -//func (s AssemblerTxList) Less(i, j int) bool { return i < j } -//func (s AssemblerTxList) Swap(i, j int) { -// s[i], s[j] = s[j], s[i] -//} - // TxByPriceAndTime implements both the sort and the heap interface, making it useful // for all at once sorting as well as individually adding and removing elements. type TxByPriceAndTime []*TxWithMinerFee From 9f5045a82b0484510e14bb183fbfd5591ca76b67 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:26:52 +0530 Subject: [PATCH 14/19] remove append impl --- core/types/transaction.go | 4 ---- core/types/withdrawal.go | 4 ---- 2 files changed, 8 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index eda68bbb3b..b5d8521b49 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -417,10 +417,6 @@ func (s Transactions) Index(i int) *Transaction { return s[i] } -func (s Transactions) Append(tx *Transaction) Transactions { - return append(s, tx) -} - // Len returns the length of s. func (s Transactions) Len() int { return len(s) } diff --git a/core/types/withdrawal.go b/core/types/withdrawal.go index 512af2c274..d1ad918f98 100644 --- a/core/types/withdrawal.go +++ b/core/types/withdrawal.go @@ -45,10 +45,6 @@ type withdrawalMarshaling struct { // Withdrawals implements DerivableList for withdrawals. type Withdrawals []*Withdrawal -func (s Withdrawals) Append(w Withdrawal) Withdrawals { - return append(s, &w) -} - // Len returns the length of s. func (s Withdrawals) Len() int { return len(s) } From 71e8aa2e44028d2464c38a6a51cd4f9ff29fb84e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:37:30 +0530 Subject: [PATCH 15/19] refactor --- beacon/engine/types.go | 44 +++++++++++++++++++++++++++++++ builder/builder.go | 2 +- eth/block-validation/api.go | 52 ++++--------------------------------- miner/payload_building.go | 2 +- miner/worker.go | 33 ++++++----------------- 5 files changed, 59 insertions(+), 74 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 00bcf26ad3..b1cc13444c 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -22,10 +22,12 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie" + boostTypes "github.com/flashbots/go-boost-utils/types" ) //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go @@ -331,3 +333,45 @@ func ExecutionPayloadV2ToBlock(payload *capella.ExecutionPayload) (*types.Block, block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(withdrawals) return block, nil } + +// bchain: copied this here to avoid circular dependency +func ExecutableDataToCapellaExecutionPayload(data *ExecutableData) (*capella.ExecutionPayload, error) { + transactionData := make([]bellatrix.Transaction, len(data.Transactions)) + for i, tx := range data.Transactions { + transactionData[i] = tx + } + + withdrawalData := make([]*capella.Withdrawal, len(data.Withdrawals)) + for i, wd := range data.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), + } + } + + baseFeePerGas := new(boostTypes.U256Str) + err := baseFeePerGas.FromBig(data.BaseFeePerGas) + if err != nil { + return nil, err + } + + return &capella.ExecutionPayload{ + ParentHash: [32]byte(data.ParentHash), + FeeRecipient: [20]byte(data.FeeRecipient), + StateRoot: data.StateRoot, + ReceiptsRoot: data.ReceiptsRoot, + LogsBloom: types.BytesToBloom(data.LogsBloom), + PrevRandao: data.Random, + BlockNumber: data.Number, + GasLimit: data.GasLimit, + GasUsed: data.GasUsed, + Timestamp: data.Timestamp, + ExtraData: data.ExtraData, + BaseFeePerGas: *baseFeePerGas, + BlockHash: [32]byte(data.BlockHash), + Transactions: transactionData, + Withdrawals: withdrawalData, + }, nil +} diff --git a/builder/builder.go b/builder/builder.go index 8f60e8d1c1..112ba04f0f 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -284,7 +284,7 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or commitedBundles, allBundles []types.SimulatedBundle, usedSbundles []types.UsedSBundle, proposerPubkey phase0.BLSPubKey, vd ValidatorData, attrs *types.BuilderPayloadAttributes) error { executableData := engine.BlockToExecutableData(block, blockValue) - payload, err := executableDataToCapellaExecutionPayload(executableData.ExecutionPayload) + payload, err := engine.ExecutableDataToCapellaExecutionPayload(executableData.ExecutionPayload) if err != nil { log.Error("could not format execution payload", "err", err) return err diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 8fa1f6a9f2..5cf2d37229 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -9,7 +9,6 @@ import ( bellatrixapi "github.com/attestantio/go-builder-client/api/bellatrix" capellaapi "github.com/attestantio/go-builder-client/api/capella" - "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/phase0" bellatrixUtil "github.com/attestantio/go-eth2-client/util/bellatrix" @@ -23,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - boostTypes "github.com/flashbots/go-boost-utils/types" ) type BlacklistedAddresses []common.Address @@ -261,55 +259,13 @@ func (b *BlockAssemblerRequest) UnmarshalJSON(data []byte) error { return nil } -// bchain: copied this here to avoid circular dependency -func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*capella.ExecutionPayload, error) { - transactionData := make([]bellatrix.Transaction, len(data.Transactions)) - for i, tx := range data.Transactions { - transactionData[i] = tx - } - - withdrawalData := make([]*capella.Withdrawal, len(data.Withdrawals)) - for i, wd := range data.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), - } - } - - baseFeePerGas := new(boostTypes.U256Str) - err := baseFeePerGas.FromBig(data.BaseFeePerGas) - if err != nil { - return nil, err - } - - return &capella.ExecutionPayload{ - ParentHash: [32]byte(data.ParentHash), - FeeRecipient: [20]byte(data.FeeRecipient), - StateRoot: data.StateRoot, - ReceiptsRoot: data.ReceiptsRoot, - LogsBloom: types.BytesToBloom(data.LogsBloom), - PrevRandao: data.Random, - BlockNumber: data.Number, - GasLimit: data.GasLimit, - GasUsed: data.GasUsed, - Timestamp: data.Timestamp, - ExtraData: data.ExtraData, - BaseFeePerGas: *baseFeePerGas, - BlockHash: [32]byte(data.BlockHash), - Transactions: transactionData, - Withdrawals: withdrawalData, - }, nil -} - func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*capella.ExecutionPayload, error) { log.Info("BlockAssembler", "tobTxs", len(params.TobTxs.Transactions), "robPayload", params.RobPayload) transactionBytes := make([][]byte, len(params.TobTxs.Transactions)) for i, txHexBytes := range params.TobTxs.Transactions { transactionBytes[i] = txHexBytes[:] } - txs, err := engine.DecodeTransactions(transactionBytes) + decodedTobTxs, err := engine.DecodeTransactions(transactionBytes) if err != nil { return nil, err } @@ -319,6 +275,8 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c return nil, err } + tobTxs := types.Transactions(decodedTobTxs) + // TODO - check for gas limits // TODO - support for payouts @@ -328,7 +286,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c // we can error out if there is a nonce gap // TODO - don't error out, but drop the duplicate tx in the ROB block seenTxMap := make(map[common.Hash]struct{}) - for _, tx := range txs { + for _, tx := range tobTxs { // If we see nonce reuse in TOB then fail if _, ok := seenTxMap[tx.Hash()]; ok { return nil, errors.New("duplicate tx") @@ -366,7 +324,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c Withdrawals: withdrawals, BlockHook: nil, AssemblerTxs: miner.AssemblerTxLists{ - TobTxs: txs, + TobTxs: &tobTxs, RobTxs: &robTxs, }, }) diff --git a/miner/payload_building.go b/miner/payload_building.go index dda330f4af..9cc89aabf0 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -32,7 +32,7 @@ import ( ) type AssemblerTxLists struct { - TobTxs []*types.Transaction + TobTxs *types.Transactions RobTxs *types.Transactions } diff --git a/miner/worker.go b/miner/worker.go index 962fb8a1cd..4216177f80 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -906,7 +906,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co profit: new(big.Int), assemblerTxs: assemblerTxs, } - if len(assemblerTxs.TobTxs) > 0 || (assemblerTxs.RobTxs != nil && assemblerTxs.RobTxs.Len() > 0) { + if (assemblerTxs.TobTxs != nil && assemblerTxs.TobTxs.Len() > 0) || (assemblerTxs.RobTxs != nil && assemblerTxs.RobTxs.Len() > 0) { env.isAssembler = true } // when 08 is processed ancestors contain 07 (quick block) @@ -1109,7 +1109,7 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem var coalescedLogs []*types.Log // first go thru TOB txs - for i, tx := range assemblerTxs.TobTxs { + for _, tx := range *assemblerTxs.TobTxs { // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1155,9 +1155,8 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem } } - // commit the ROB txs - i := 0 - for { + // now go thru ROB txs + for _, tx := range *assemblerTxs.RobTxs { // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { @@ -1170,12 +1169,6 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem break } - if i >= assemblerTxs.RobTxs.Len() { - break - } - - tx := assemblerTxs.RobTxs.Index(i) - // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) @@ -1207,8 +1200,6 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // nonce-too-high clause will prevent us from executing in vain). log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) } - - i += 1 } if !w.isRunning() && len(coalescedLogs) > 0 { @@ -1468,22 +1459,14 @@ func (w *worker) fillTransactionsSelectAlgo(interrupt *int32, env *environment) func (w *worker) fillAssemblerTransactions(interrupt *int32, env *environment) ([]types.SimulatedBundle, []types.SimulatedBundle, map[common.Hash]struct{}, error) { assemblerTxs := env.assemblerTxs - totalTxsToAssemble := assemblerTxs.RobTxs.Len() + len(assemblerTxs.TobTxs) + totalTxsToAssemble := assemblerTxs.RobTxs.Len() + assemblerTxs.TobTxs.Len() mempoolHashes := make(map[common.Hash]struct{}, totalTxsToAssemble) - for _, tx := range assemblerTxs.TobTxs { + for _, tx := range *assemblerTxs.TobTxs { mempoolHashes[tx.Hash()] = struct{}{} } - i := 0 - for { - if i >= assemblerTxs.RobTxs.Len() { - break - } - - robTx := assemblerTxs.RobTxs.Index(i) - mempoolHashes[robTx.Hash()] = struct{}{} - - i += 1 + for _, tx := range *assemblerTxs.RobTxs { + mempoolHashes[tx.Hash()] = struct{}{} } if totalTxsToAssemble > 0 { From 03002bf59954bcb6d6d1880fe6bc4efd40c82d6e Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:38:52 +0530 Subject: [PATCH 16/19] more refactor --- builder/builder.go | 42 ------------------------------------- eth/block-validation/api.go | 2 +- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index 112ba04f0f..c7032284f4 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -13,7 +13,6 @@ import ( capellaapi "github.com/attestantio/go-builder-client/api/capella" apiv1 "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/phase0" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -512,44 +511,3 @@ func executableDataToExecutionPayload(data *engine.ExecutableData) (*bellatrix.E Transactions: transactionData, }, nil } - -func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*capella.ExecutionPayload, error) { - transactionData := make([]bellatrix.Transaction, len(data.Transactions)) - for i, tx := range data.Transactions { - transactionData[i] = tx - } - - withdrawalData := make([]*capella.Withdrawal, len(data.Withdrawals)) - for i, wd := range data.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), - } - } - - baseFeePerGas := new(boostTypes.U256Str) - err := baseFeePerGas.FromBig(data.BaseFeePerGas) - if err != nil { - return nil, err - } - - return &capella.ExecutionPayload{ - ParentHash: [32]byte(data.ParentHash), - FeeRecipient: [20]byte(data.FeeRecipient), - StateRoot: data.StateRoot, - ReceiptsRoot: data.ReceiptsRoot, - LogsBloom: types.BytesToBloom(data.LogsBloom), - PrevRandao: data.Random, - BlockNumber: data.Number, - GasLimit: data.GasLimit, - GasUsed: data.GasUsed, - Timestamp: data.Timestamp, - ExtraData: data.ExtraData, - BaseFeePerGas: *baseFeePerGas, - BlockHash: [32]byte(data.BlockHash), - Transactions: transactionData, - Withdrawals: withdrawalData, - }, nil -} diff --git a/eth/block-validation/api.go b/eth/block-validation/api.go index 5cf2d37229..966903c4ef 100644 --- a/eth/block-validation/api.go +++ b/eth/block-validation/api.go @@ -339,7 +339,7 @@ func (api *BlockValidationAPI) BlockAssembler(params *BlockAssemblerRequest) (*c return nil, errors.New("nil execution payload") } - finalPayload, err := executableDataToCapellaExecutionPayload(resolvedBlock.ExecutionPayload) + finalPayload, err := engine.ExecutableDataToCapellaExecutionPayload(resolvedBlock.ExecutionPayload) return finalPayload, nil } From 27ac613b5f72299ed2161ba4e0321e24e566cfa4 Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:40:11 +0530 Subject: [PATCH 17/19] remove out commented code --- eth/block-validation/api_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/eth/block-validation/api_test.go b/eth/block-validation/api_test.go index c6b6f3c79d..36568eed64 100644 --- a/eth/block-validation/api_test.go +++ b/eth/block-validation/api_test.go @@ -1030,19 +1030,6 @@ func TestValidateBuilderSubmissionV2_CoinbasePaymentDefault(t *testing.T) { require.ErrorContains(t, api.ValidateBuilderSubmissionV2(req), "payment") } -//func TestBlockAssembler(t *testing.T) { -// genesis, preMergeBlocks := generatePreMergeChain(20) -// lastBlock := preMergeBlocks[len(preMergeBlocks)-1] -// time := lastBlock.Time() + 5 -// genesis.Config.ShanghaiTime = &time -// n, ethservice := startEthService(t, genesis, preMergeBlocks) -// ethservice.Merger().ReachTTD() -// defer n.Close() -// -// apiNoBlock := NewBlockValidationAPI(ethservice, nil, false) -// baseFee := misc.CalcBaseFee(ethservice.BlockChain().Config(), lastBlock.Header()) -//} - func TestValidateBuilderSubmissionV2_Blocklist(t *testing.T) { genesis, preMergeBlocks := generatePreMergeChain(20) lastBlock := preMergeBlocks[len(preMergeBlocks)-1] From c86b160c4827d90d6e3a62b58e5039b73365949f Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:45:30 +0530 Subject: [PATCH 18/19] refactor --- miner/multi_worker.go | 65 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/miner/multi_worker.go b/miner/multi_worker.go index d528f5a8bf..44ff09acf9 100644 --- a/miner/multi_worker.go +++ b/miner/multi_worker.go @@ -2,7 +2,6 @@ package miner import ( "errors" - "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -87,43 +86,49 @@ func (w *multiWorker) disablePreseal() { } } +// buildPayload builds the payload according to the provided parameters. func (w *multiWorker) payloadAssembler(args *BuildPayloadArgs) (*Payload, error) { // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. - var empty *types.Block - for _, worker := range w.workers { - var err error - empty, _, err = worker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, nil, AssemblerTxLists{}) - if err != nil { - log.Error("could not start async block construction", "isFlashbotsWorker", worker.flashbots.isFlashbots, "#bundles", worker.flashbots.maxMergedBundles) - continue - } - break - } - - if empty == nil { - return nil, errors.New("no worker could build an empty block") - } - - // Construct a payload object for return. - payload := newPayload(empty, args.Id()) - - if len(w.workers) == 0 { - return payload, nil - } - - var tobBlock *types.Block - var fees *big.Int - var err error - start := time.Now() - tobBlock, fees, err = w.regularWorker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) + empty, _, err := w.regularWorker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, true, args.BlockHook, args.AssemblerTxs) if err != nil { - log.Error("could not start async block construction", "err", err) return nil, err } - payload.update(tobBlock, fees, time.Since(start)) + // Construct a payload object for return. + payload := newPayload(empty, args.Id()) + // Spin up a routine for updating the payload in background. This strategy + // can maximum the revenue for including transactions with highest fee. + go func() { + // Setup the timer for re-building the payload. The initial clock is kept + // for triggering process immediately. + timer := time.NewTimer(0) + defer timer.Stop() + + // Setup the timer for terminating the process if SECONDS_PER_SLOT (12s in + // the Mainnet configuration) have passed since the point in time identified + // by the timestamp parameter. + endTimer := time.NewTimer(time.Second * 12) + + for { + select { + case <-timer.C: + start := time.Now() + block, fees, err := w.regularWorker.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.GasLimit, args.Random, args.Withdrawals, false, args.BlockHook, args.AssemblerTxs) + if err == nil { + payload.update(block, fees, time.Since(start)) + } + timer.Reset(w.regularWorker.recommit) + case <-payload.stop: + log.Info("Stopping work on payload", "id", payload.id, "reason", "delivery") + return + case <-endTimer.C: + log.Info("Stopping work on payload", "id", payload.id, "reason", "timeout") + return + } + } + }() return payload, nil } From 60c1aa3264d7a5c37894aff966a0a580362a1c1c Mon Sep 17 00:00:00 2001 From: Bharath Vedartham Date: Tue, 12 Sep 2023 17:50:28 +0530 Subject: [PATCH 19/19] refactor --- miner/worker.go | 138 +++++++++++++++++++----------------------------- 1 file changed, 54 insertions(+), 84 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 4216177f80..86bead5597 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1101,6 +1101,54 @@ func (w *worker) commitBundle(env *environment, txs types.Transactions, interrup return nil } +func (w *worker) commitTransactionWrapper(env *environment, interrupt *int32, tx *types.Transaction, coalescedLogs []*types.Log) error { + // Check interruption signal and abort building if it's fired. + if interrupt != nil { + if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { + return signalToErr(signal) + } + } + // If we don't have enough gas for any further transactions then we're done. + if env.gasPool.Gas() < params.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) + return nil + } + + // Error may be ignored here. The error has already been checked + // during transaction acceptance is the transaction pool. + from, _ := types.Sender(env.signer, tx) + logs, err := w.commitTransaction(env, tx) + switch { + case errors.Is(err, core.ErrGasLimitReached): + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + + case errors.Is(err, core.ErrNonceTooLow): + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + + case errors.Is(err, core.ErrNonceTooHigh): + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) + + case errors.Is(err, nil): + // Everything ok, collect the logs and shift in the next transaction from the same account + coalescedLogs = append(coalescedLogs, logs...) + env.tcount++ + + case errors.Is(err, types.ErrTxTypeNotSupported): + // Pop the unsupported transaction without shifting in the next from the account + log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + } + + return nil +} + func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs AssemblerTxLists, interrupt *int32) error { gasLimit := env.header.GasLimit if env.gasPool == nil { @@ -1110,95 +1158,17 @@ func (w *worker) commitAssemblyTransactions(env *environment, assemblerTxs Assem // first go thru TOB txs for _, tx := range *assemblerTxs.TobTxs { - // Check interruption signal and abort building if it's fired. - if interrupt != nil { - if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { - return signalToErr(signal) - } - } - // If we don't have enough gas for any further transactions then we're done. - if env.gasPool.Gas() < params.TxGas { - log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) - break - } - - // Error may be ignored here. The error has already been checked - // during transaction acceptance is the transaction pool. - from, _ := types.Sender(env.signer, tx) - logs, err := w.commitTransaction(env, tx) - switch { - case errors.Is(err, core.ErrGasLimitReached): - // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace("Gas limit exceeded for current block", "sender", from) - - case errors.Is(err, core.ErrNonceTooLow): - // New head notification data race between the transaction pool and miner, shift - log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - - case errors.Is(err, core.ErrNonceTooHigh): - // Reorg notification data race between the transaction pool and miner, skip account = - log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) - - case errors.Is(err, nil): - // Everything ok, collect the logs and shift in the next transaction from the same account - coalescedLogs = append(coalescedLogs, logs...) - env.tcount++ - - case errors.Is(err, types.ErrTxTypeNotSupported): - // Pop the unsupported transaction without shifting in the next from the account - log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) - - default: - // Strange error, discard the transaction and get the next in line (note, the - // nonce-too-high clause will prevent us from executing in vain). - log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + err := w.commitTransactionWrapper(env, interrupt, tx, coalescedLogs) + if err != nil { + return err } } // now go thru ROB txs for _, tx := range *assemblerTxs.RobTxs { - // Check interruption signal and abort building if it's fired. - if interrupt != nil { - if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { - return signalToErr(signal) - } - } - // If we don't have enough gas for any further transactions then we're done. - if env.gasPool.Gas() < params.TxGas { - log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) - break - } - - // Error may be ignored here. The error has already been checked - // during transaction acceptance is the transaction pool. - from, _ := types.Sender(env.signer, tx) - logs, err := w.commitTransaction(env, tx) - switch { - case errors.Is(err, core.ErrGasLimitReached): - // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace("Gas limit exceeded for current block", "sender", from) - - case errors.Is(err, core.ErrNonceTooLow): - // New head notification data race between the transaction pool and miner, shift - log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - - case errors.Is(err, core.ErrNonceTooHigh): - // Reorg notification data race between the transaction pool and miner, skip account = - log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) - - case errors.Is(err, nil): - // Everything ok, collect the logs and shift in the next transaction from the same account - coalescedLogs = append(coalescedLogs, logs...) - env.tcount++ - - case errors.Is(err, types.ErrTxTypeNotSupported): - // Pop the unsupported transaction without shifting in the next from the account - log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) - - default: - // Strange error, discard the transaction and get the next in line (note, the - // nonce-too-high clause will prevent us from executing in vain). - log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + err := w.commitTransactionWrapper(env, interrupt, tx, coalescedLogs) + if err != nil { + return err } }