Skip to content

Commit

Permalink
fix: avoid producing empty block when pending transactions is high
Browse files Browse the repository at this point in the history
This commit adapts BSC PR: bnb-chain/bsc#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.
  • Loading branch information
Bui Quang Minh committed Dec 30, 2022
1 parent 7df949f commit cf26e62
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 14 deletions.
1 change: 1 addition & 0 deletions cmd/ronin/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.MinerExtraDataFlag,
utils.MinerRecommitIntervalFlag,
utils.MinerNoVerifyFlag,
utils.MinerBlockProduceLeftoverFlag,
},
},
{
Expand Down
8 changes: 8 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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!")
}
Expand Down
5 changes: 5 additions & 0 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 7 additions & 5 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
package ethconfig

import (
"github.com/ethereum/go-ethereum/consensus/consortium"
"github.com/ethereum/go-ethereum/internal/ethapi"
"math/big"
"os"
"os/user"
"path/filepath"
"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"
Expand Down Expand Up @@ -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,
Expand Down
23 changes: 14 additions & 9 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit cf26e62

Please sign in to comment.