miner: build block with mandatory inclusion list

This commit is contained in:
Marius van der Wijden 2024-03-21 05:26:12 +01:00
parent 4cce9f9970
commit 226fe5c203
3 changed files with 74 additions and 38 deletions

View file

@ -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
}

View file

@ -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 {

View file

@ -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)