From 226fe5c203db69789381cabf9ee23e76dd3aa444 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 21 Mar 2024 05:26:12 +0100 Subject: [PATCH] miner: build block with mandatory inclusion list --- eth/catalyst/api.go | 20 ++++++++++------ miner/payload_building.go | 49 +++++++++++++++++++++------------------ miner/worker.go | 43 +++++++++++++++++++++++++++------- 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index c6e6fccca9..1f093e2589 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -136,6 +136,8 @@ type ConsensusAPI struct { forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method newPayloadLock sync.Mutex // Lock for the NewPayload method + + coolMapThatIsNotAMemLeak map[common.Hash][]*types.Transaction } // NewConsensusAPI creates a new consensus api for the given backend. @@ -341,6 +343,8 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl } // Set the finalized block api.eth.BlockChain().SetFinalized(finalBlock.Header()) + // Clear the inclusionList for that block + delete(api.coolMapThatIsNotAMemLeak, update.FinalizedBlockHash) } // Check if the safe block hash is in our canonical tree, if not something is wrong if update.SafeBlockHash != (common.Hash{}) { @@ -361,13 +365,14 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl // will replace it arbitrarily many times in between. if payloadAttributes != nil { args := &miner.BuildPayloadArgs{ - Parent: update.HeadBlockHash, - Timestamp: payloadAttributes.Timestamp, - FeeRecipient: payloadAttributes.SuggestedFeeRecipient, - Random: payloadAttributes.Random, - Withdrawals: payloadAttributes.Withdrawals, - BeaconRoot: payloadAttributes.BeaconRoot, - Version: payloadVersion, + Parent: update.HeadBlockHash, + Timestamp: payloadAttributes.Timestamp, + FeeRecipient: payloadAttributes.SuggestedFeeRecipient, + Random: payloadAttributes.Random, + Withdrawals: payloadAttributes.Withdrawals, + BeaconRoot: payloadAttributes.BeaconRoot, + Version: payloadVersion, + InclusionList: api.coolMapThatIsNotAMemLeak[update.HeadBlockHash], } id := args.Id() // If we already are busy generating this work, then we do not need @@ -928,6 +933,7 @@ func (api *ConsensusAPI) NewInclusionListV1(addresses []common.Address, transact if err := eip7547.VerifyInclusionList(api.eth.BlockChain(), block, signer, txs); err != nil { return inclusionListError(err.Error()), nil } + api.coolMapThatIsNotAMemLeak[parentBlockHash] = txs return &engine.InclusionListStatusV1{Status: engine.VALID}, nil } diff --git a/miner/payload_building.go b/miner/payload_building.go index d027cd1e1f..b42f3dff59 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/cancun.md#payloadattributesv3 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 - Random common.Hash // The provided randomness value - Withdrawals types.Withdrawals // The provided withdrawals - BeaconRoot *common.Hash // The provided beaconRoot (Cancun) - Version engine.PayloadVersion // Versioning byte for payload id calculation. + 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 + Random common.Hash // The provided randomness value + Withdrawals types.Withdrawals // The provided withdrawals + BeaconRoot *common.Hash // The provided beaconRoot (Cancun) + Version engine.PayloadVersion // Versioning byte for payload id calculation. + InclusionList []*types.Transaction // Mandatory Inclusion List } // Id computes an 8-byte identifier by hashing the components of the payload arguments. @@ -181,14 +182,15 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. emptyParams := &generateParams{ - timestamp: args.Timestamp, - forceTime: true, - parentHash: args.Parent, - coinbase: args.FeeRecipient, - random: args.Random, - withdrawals: args.Withdrawals, - beaconRoot: args.BeaconRoot, - noTxs: true, + timestamp: args.Timestamp, + forceTime: true, + parentHash: args.Parent, + coinbase: args.FeeRecipient, + random: args.Random, + withdrawals: args.Withdrawals, + beaconRoot: args.BeaconRoot, + noTxs: true, + inclusionList: args.InclusionList, } empty := miner.generateWork(emptyParams) if empty.err != nil { @@ -212,14 +214,15 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { endTimer := time.NewTimer(time.Second * 12) fullParams := &generateParams{ - timestamp: args.Timestamp, - forceTime: true, - parentHash: args.Parent, - coinbase: args.FeeRecipient, - random: args.Random, - withdrawals: args.Withdrawals, - beaconRoot: args.BeaconRoot, - noTxs: false, + timestamp: args.Timestamp, + forceTime: true, + parentHash: args.Parent, + coinbase: args.FeeRecipient, + random: args.Random, + withdrawals: args.Withdrawals, + beaconRoot: args.BeaconRoot, + noTxs: false, + inclusionList: args.InclusionList, } for { diff --git a/miner/worker.go b/miner/worker.go index f22242841f..31b563917f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -77,14 +77,15 @@ type newPayloadResult struct { // generateParams wraps various of settings for generating sealing task. type generateParams struct { - timestamp uint64 // The timestamp 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 - random common.Hash // The randomness generated by beacon chain, empty before the merge - withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field) - beaconRoot *common.Hash // The beacon root (cancun field). - noTxs bool // Flag whether an empty block without any transaction is expected + timestamp uint64 // The timestamp 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 + random common.Hash // The randomness generated by beacon chain, empty before the merge + withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field) + beaconRoot *common.Hash // The beacon root (cancun field). + noTxs bool // Flag whether an empty block without any transaction is expected + inclusionList []*types.Transaction // Mandatory transactions to include } // generateWork generates a sealing block based on the given parameters. @@ -93,6 +94,19 @@ func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { if err != nil { return &newPayloadResult{err: err} } + // apply inclusion list + if len(params.inclusionList) != 0 { + interrupt := new(atomic.Int32) + timer := time.AfterFunc(miner.config.Recommit, func() { + interrupt.Store(commitInterruptTimeout) + }) + defer timer.Stop() + + err := miner.applyInclusionList(interrupt, work, params.inclusionList) + if err != nil { + log.Error("Unexpected error while applying inclusion list: %v, noTxs: %v", err, params.noTxs) + } + } if !params.noTxs { interrupt := new(atomic.Int32) timer := time.AfterFunc(miner.config.Recommit, func() { @@ -431,6 +445,19 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) return nil } +func (miner *Miner) applyInclusionList(interrupt *atomic.Int32, env *environment, inclusionList []*types.Transaction) error { + txs := make(map[common.Address][]*txpool.LazyTransaction) + for _, tx := range inclusionList { + signer, _ := env.signer.Sender(tx) + txs[signer] = append(txs[signer], &txpool.LazyTransaction{Tx: tx}) + } + plainTxs := newTransactionsByPriceAndNonce(env.signer, txs, env.header.BaseFee) + if err := miner.commitTransactions(env, plainTxs, new(transactionsByPriceAndNonce), interrupt); err != nil { + return err + } + return nil +} + // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order. func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int { feesWei := new(big.Int)