feat(worker): seal block early if we're over target block time (#706)

* feat(worker): seal block early if we're over target block time

* use current.header.Time

* typo

* break instead of return
This commit is contained in:
Péter Garamvölgyi 2024-04-18 10:44:59 +08:00 committed by GitHub
parent d81a3308b1
commit 62926d4649
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 1 deletions

View file

@ -251,6 +251,7 @@ type worker struct {
skipSealHook func(*task) bool // Method to decide whether skipping the sealing.
fullTaskHook func() // Method to call before pushing the full sealing task.
resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
beforeTxHook func() // Method to call before processing a transaction.
}
func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker {
@ -1034,6 +1035,10 @@ func (w *worker) commitTransactions(txs types.OrderedTransactionSet, coinbase co
var loops int64
loop:
for {
if w.beforeTxHook != nil {
w.beforeTxHook()
}
loops++
// In the following three cases, we will interrupt the execution of the transaction.
// (1) new head block event arrival, the interrupt signal is 1
@ -1055,6 +1060,12 @@ loop:
}
return atomic.LoadInt32(interrupt) == commitInterruptNewHead, circuitCapacityReached
}
// seal block early if we're over time
// note: current.header.Time = max(parent.Time + cliquePeriod, now())
if w.current.tcount > 0 && w.chainConfig.Clique != nil && uint64(time.Now().Unix()) > w.current.header.Time {
circuitCapacityReached = true // skip subsequent invocations of commitTransactions
break
}
// If we don't have enough gas for any further transactions then we're done
if w.current.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas)

View file

@ -129,6 +129,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
switch e := engine.(type) {
case *clique.Clique:
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
gspec.Timestamp = uint64(time.Now().Unix())
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
return crypto.Sign(crypto.Keccak256(data), testBankKey)
@ -1253,3 +1254,67 @@ func TestSkippedTransactionDatabaseEntries(t *testing.T) {
}
})
}
func TestSealBlockAfterCliquePeriod(t *testing.T) {
assert := assert.New(t)
var (
engine consensus.Engine
chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase()
)
chainConfig = params.AllCliqueProtocolChanges
chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
engine = clique.New(chainConfig.Clique, db)
w, b := newTestWorker(t, chainConfig, engine, db, 0)
defer w.close()
// This test chain imports the mined blocks.
b.genesis.MustCommit(db)
chain, _ := core.NewBlockChain(db, nil, b.chain.Config(), engine, vm.Config{
Debug: true,
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
defer chain.Stop()
// Ignore empty commit here for less noise.
w.skipSealHook = func(task *task) bool {
return len(task.receipts) == 0
}
// Add artificial delay to transaction processing.
w.beforeTxHook = func() {
time.Sleep(time.Duration(chainConfig.Clique.Period) * 1 * time.Second)
}
// Wait for mined blocks.
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
defer sub.Unsubscribe()
// Insert 2 non-l1msg txs
b.txPool.AddLocal(b.newRandomTx(true))
b.txPool.AddLocal(b.newRandomTx(false))
// Start mining!
w.start()
select {
case ev := <-sub.Chan():
block := ev.Data.(core.NewMinedBlockEvent).Block
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
}
assert.Equal(1, len(block.Transactions())) // only packed 1 tx, not 2
case <-time.After(5 * time.Second):
t.Fatalf("timeout")
}
select {
case ev := <-sub.Chan():
block := ev.Data.(core.NewMinedBlockEvent).Block
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
}
assert.Equal(1, len(block.Transactions()))
case <-time.After(5 * time.Second):
t.Fatalf("timeout")
}
}

View file

@ -24,7 +24,7 @@ import (
const (
VersionMajor = 5 // Major version component of the current release
VersionMinor = 2 // Minor version component of the current release
VersionPatch = 4 // Patch version component of the current release
VersionPatch = 5 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)