From 62926d46495bdc9ab2138c2fd24a2b206508f936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 18 Apr 2024 10:44:59 +0800 Subject: [PATCH] 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 --- miner/worker.go | 11 ++++++++ miner/worker_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++ params/version.go | 2 +- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/miner/worker.go b/miner/worker.go index 41b0167abf..7764786fcd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -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) diff --git a/miner/worker_test.go b/miner/worker_test.go index 533c4289a9..7e3cb337b9 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -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 = ¶ms.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") + } +} diff --git a/params/version.go b/params/version.go index 93d9bc4132..312f2dae31 100644 --- a/params/version.go +++ b/params/version.go @@ -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 )