From cf26e62ba3fed69b501f4b4dbe4848de56b30ba0 Mon Sep 17 00:00:00 2001 From: Bui Quang Minh Date: Fri, 30 Dec 2022 10:52:05 +0700 Subject: [PATCH] fix: avoid producing empty block when pending transactions is high This commit adapts BSC PR: https://github.com/bnb-chain/bsc/pull/112 to Ronin. An empty block is pre-sealed by default. It's expected that a new block with transactions is committed and abort the empty block producer if there are pending transactions. However, with the low block time, in case there are a lot of pending transactions, the commit transactions time exceeds the time an empty block is produced. As a result, block with transactions is not produced but an empty block and no real transactions is mined. --- cmd/ronin/usage.go | 1 + cmd/utils/flags.go | 8 ++++++++ eth/api.go | 5 +++++ eth/ethconfig/config.go | 12 +++++++----- miner/miner.go | 23 ++++++++++++++--------- miner/worker.go | 26 ++++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/cmd/ronin/usage.go b/cmd/ronin/usage.go index 610107bfe6..3953c31269 100644 --- a/cmd/ronin/usage.go +++ b/cmd/ronin/usage.go @@ -191,6 +191,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.MinerExtraDataFlag, utils.MinerRecommitIntervalFlag, utils.MinerNoVerifyFlag, + utils.MinerBlockProduceLeftoverFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 532df59056..51e15e2eb8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -473,6 +473,11 @@ var ( Name: "miner.noverify", Usage: "Disable remote sealing verification", } + MinerBlockProduceLeftoverFlag = cli.DurationFlag{ + Name: "miner.leftover", + Usage: "The interval block with transactions needs committing before empty block is produced", + Value: ethconfig.Defaults.Miner.BlockProduceLeftOver, + } // Account settings UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", @@ -1483,6 +1488,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) { if ctx.GlobalIsSet(MinerNoVerifyFlag.Name) { cfg.Noverify = ctx.GlobalBool(MinerNoVerifyFlag.Name) } + if ctx.GlobalIsSet(MinerBlockProduceLeftoverFlag.Name) { + cfg.BlockProduceLeftOver = ctx.GlobalDuration(MinerBlockProduceLeftoverFlag.Name) + } if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) { log.Warn("The generic --miner.gastarget flag is deprecated and will be removed in the future!") } diff --git a/eth/api.go b/eth/api.go index f81dfa922b..f6df8e7b86 100644 --- a/eth/api.go +++ b/eth/api.go @@ -147,6 +147,11 @@ func (api *PrivateMinerAPI) SetRecommitInterval(interval int) { api.e.Miner().SetRecommitInterval(time.Duration(interval) * time.Millisecond) } +// SetBlockProducerLeftover updates the interval for block producer leftover in milliseconds +func (api *PrivateMinerAPI) SetBlockProducerLeftover(interval int) { + api.e.Miner().SetBlockProducerLeftover(time.Duration(interval) * time.Millisecond) +} + // PrivateAdminAPI is the collection of Ethereum full node-related APIs // exposed over the private admin endpoint. type PrivateAdminAPI struct { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 3a93763d6f..272927f59d 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,8 +18,6 @@ package ethconfig import ( - "github.com/ethereum/go-ethereum/consensus/consortium" - "github.com/ethereum/go-ethereum/internal/ethapi" "math/big" "os" "os/user" @@ -27,6 +25,9 @@ import ( "runtime" "time" + "github.com/ethereum/go-ethereum/consensus/consortium" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/clique" @@ -85,9 +86,10 @@ var Defaults = Config{ TrieTimeout: 60 * time.Minute, SnapshotCache: 102, Miner: miner.Config{ - GasCeil: 8000000, - GasPrice: big.NewInt(params.GWei), - Recommit: 3 * time.Second, + GasCeil: 8000000, + GasPrice: big.NewInt(params.GWei), + Recommit: 3 * time.Second, + BlockProduceLeftOver: 200 * time.Millisecond, }, TxPool: core.DefaultTxPoolConfig, RPCGasCap: 50000000, diff --git a/miner/miner.go b/miner/miner.go index 1c33b3bd28..fa312f7860 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -43,15 +43,16 @@ type Backend interface { // Config is the configuration parameters of mining. type Config struct { - Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) - Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). - NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages - ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner - GasFloor uint64 // Target gas floor for mined blocks. - GasCeil uint64 // Target gas ceiling for mined blocks. - GasPrice *big.Int // Minimum gas price for mining a transaction - Recommit time.Duration // The time interval for miner to re-create mining work. - Noverify bool // Disable remote mining solution verification(only useful in ethash). + Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) + Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). + NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages + ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner + GasFloor uint64 // Target gas floor for mined blocks. + GasCeil uint64 // Target gas ceiling for mined blocks. + GasPrice *big.Int // Minimum gas price for mining a transaction + Recommit time.Duration // The time interval for miner to re-create mining work. + Noverify bool // Disable remote mining solution verification(only useful in ethash). + BlockProduceLeftOver time.Duration } // Miner creates blocks and searches for proof-of-work values. @@ -216,6 +217,10 @@ func (miner *Miner) SetGasCeil(ceil uint64) { miner.worker.setGasCeil(ceil) } +func (miner *Miner) SetBlockProducerLeftover(interval time.Duration) { + miner.worker.setBlockProducerLeftover(interval) +} + // EnablePreseal turns on the preseal mining feature. It's enabled by default. // Note this function shouldn't be exposed to API, it's unnecessary for users // (miners) to actually know the underlying detail. It's only for outside project diff --git a/miner/worker.go b/miner/worker.go index 02c29a03b1..e7a033d975 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -287,6 +287,12 @@ func (w *worker) setRecommitInterval(interval time.Duration) { w.resubmitIntervalCh <- interval } +func (w *worker) setBlockProducerLeftover(interval time.Duration) { + w.mu.Lock() + defer w.mu.Unlock() + w.config.BlockProduceLeftOver = interval +} + // disablePreseal disables pre-sealing mining feature func (w *worker) disablePreseal() { atomic.StoreUint32(&w.noempty, 1) @@ -816,8 +822,28 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin } var coalescedLogs []*types.Log + var timer *time.Timer + + // This timer is only shipped after Buba hardfork + // When it is nearly the time an empty block is produced, + // we break the commit transactions process, allow the + // block with transactions to be produced and abort the + // empty block producer. + if w.chainConfig.IsBuba(w.current.header.Number) { + duration := time.Until(time.Unix(int64(w.current.header.Time), 0)) - w.config.BlockProduceLeftOver + timer = time.NewTimer(duration) + } +Loop: for { + if timer != nil { + select { + case <-timer.C: + break Loop + default: + } + } + // In the following three cases, we will interrupt the execution of the transaction. // (1) new head block event arrival, the interrupt signal is 1 // (2) worker start or restart, the interrupt signal is 1