Skip to content

Commit 3d88e87

Browse files
sync recent miner/ccc changes (#1062)
* refactor: eliminate double re-execution in AsyncChecker (#1036) * fix: make reorg mode explicit (#1049) * fix: avoid committing empty blocks after the deadline (#1051) * fix: initialize pending block with an empty block (#1052) * fix: reuse timestamp for blocks failing CCC (#1031) we assume that same height wont trigger multiple reorgs to be able to put an upper bound on the reorg depth. We rely on the fact that AsyncChecker executes transactions one-by-one and tells worker the safe set of transactions to include in the replacement block that wont trigger another error on the same block. If worker changes the timestamp and that causes significant changes to the execution flow of included transactions; we might have a height where multiple reorgs happen. --------- Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com>
1 parent 01eefe5 commit 3d88e87

File tree

2 files changed

+37
-49
lines changed

2 files changed

+37
-49
lines changed

miner/scroll_worker.go

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ type work struct {
9191
cccLogger *ccc.Logger
9292
vmConfig vm.Config
9393

94+
reorging bool
9495
reorgReason error
9596

9697
// accumulated state
@@ -287,7 +288,7 @@ func (w *worker) mainLoop() {
287288
var retryableCommitError *retryableCommitError
288289
if errors.As(err, &retryableCommitError) {
289290
log.Warn("failed to commit to a block, retrying", "err", err)
290-
if _, err = w.tryCommitNewWork(time.Now(), w.current.header.ParentHash, w.current.reorgReason); err != nil {
291+
if _, err = w.tryCommitNewWork(time.Now(), w.current.header.ParentHash, w.current.reorging, w.current.reorgReason); err != nil {
291292
continue
292293
}
293294
} else if err != nil {
@@ -305,21 +306,20 @@ func (w *worker) mainLoop() {
305306
return
306307
}
307308
}
308-
309-
_, err = w.tryCommitNewWork(time.Now(), w.chain.CurrentHeader().Hash(), nil)
309+
_, err = w.tryCommitNewWork(time.Now(), w.chain.CurrentHeader().Hash(), false, nil)
310310
case trigger := <-w.reorgCh:
311311
idleTimer.UpdateSince(idleStart)
312312
err = w.handleReorg(&trigger)
313313
case chainHead := <-w.chainHeadCh:
314314
idleTimer.UpdateSince(idleStart)
315315
if w.isCanonical(chainHead.Block.Header()) {
316-
_, err = w.tryCommitNewWork(time.Now(), chainHead.Block.Hash(), nil)
316+
_, err = w.tryCommitNewWork(time.Now(), chainHead.Block.Hash(), false, nil)
317317
}
318318
case <-w.current.deadlineCh():
319319
idleTimer.UpdateSince(idleStart)
320320
w.current.deadlineReached = true
321321
if len(w.current.txs) > 0 {
322-
_, err = w.commit(false)
322+
_, err = w.commit()
323323
}
324324
case ev := <-w.txsCh:
325325
idleTimer.UpdateSince(idleStart)
@@ -330,8 +330,8 @@ func (w *worker) mainLoop() {
330330
// be automatically eliminated.
331331
if w.current != nil {
332332
shouldCommit, _ := w.processTxnSlice(ev.Txs)
333-
if shouldCommit || w.current.deadlineReached {
334-
_, err = w.commit(false)
333+
if shouldCommit || (w.current.deadlineReached && len(w.current.txs) > 0) {
334+
_, err = w.commit()
335335
}
336336
}
337337
w.newTxs.Add(int32(len(ev.Txs)))
@@ -370,7 +370,7 @@ func (w *worker) collectPendingL1Messages(startIndex uint64) []types.L1MessageTx
370370
}
371371

372372
// newWork
373-
func (w *worker) newWork(now time.Time, parentHash common.Hash, reorgReason error) error {
373+
func (w *worker) newWork(now time.Time, parentHash common.Hash, reorging bool, reorgReason error) error {
374374
parent := w.chain.GetBlockByHash(parentHash)
375375
header := &types.Header{
376376
ParentHash: parent.Hash(),
@@ -380,6 +380,13 @@ func (w *worker) newWork(now time.Time, parentHash common.Hash, reorgReason erro
380380
Time: uint64(now.Unix()),
381381
}
382382

383+
if reorgReason != nil {
384+
// if we are replacing a failing block, reuse the timestamp to make sure
385+
// the information we get from AsyncChecker is reliable. Changing timestamp
386+
// might alter execution flow of reorged transactions.
387+
header.Time = w.chain.GetHeaderByNumber(header.Number.Uint64()).Time
388+
}
389+
383390
parentState, err := w.chain.StateAt(parent.Root())
384391
if err != nil {
385392
return fmt.Errorf("failed to fetch parent state: %w", err)
@@ -431,14 +438,19 @@ func (w *worker) newWork(now time.Time, parentHash common.Hash, reorgReason erro
431438
coalescedLogs: []*types.Log{},
432439
gasPool: new(core.GasPool).AddGas(header.GasLimit),
433440
nextL1MsgIndex: nextL1MsgIndex,
441+
reorging: reorging,
434442
reorgReason: reorgReason,
435443
}
444+
445+
// initiliaze pending block with an empty block to make sure we always have
446+
// a pending block to serve RPC requests
447+
w.updateSnapshot()
436448
return nil
437449
}
438450

439451
// tryCommitNewWork
440-
func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorgReason error) (common.Hash, error) {
441-
err := w.newWork(now, parent, reorgReason)
452+
func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorging bool, reorgReason error) (common.Hash, error) {
453+
err := w.newWork(now, parent, reorging, reorgReason)
442454
if err != nil {
443455
return common.Hash{}, fmt.Errorf("failed creating new work: %w", err)
444456
}
@@ -449,8 +461,7 @@ func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorgReason
449461
}
450462

451463
// check if we are reorging
452-
reorging := w.chain.GetBlockByNumber(w.current.header.Number.Uint64()) != nil
453-
if !shouldCommit && reorging {
464+
if !shouldCommit && w.current.reorging {
454465
shouldCommit, err = w.processReorgedTxns(w.current.reorgReason)
455466
}
456467
if err != nil {
@@ -468,7 +479,7 @@ func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorgReason
468479
// if reorging, force committing even if we are not "running"
469480
// this can happen when sequencer is instructed to shutdown while handling a reorg
470481
// we should make sure reorg is not interrupted
471-
if blockHash, err := w.commit(reorging); err != nil {
482+
if blockHash, err := w.commit(); err != nil {
472483
return common.Hash{}, fmt.Errorf("failed committing new work: %w", err)
473484
} else {
474485
return blockHash, nil
@@ -595,6 +606,10 @@ func (w *worker) processTxnSlice(txns types.Transactions) (bool, error) {
595606
// processReorgedTxns
596607
func (w *worker) processReorgedTxns(reason error) (bool, error) {
597608
reorgedBlock := w.chain.GetBlockByNumber(w.current.header.Number.Uint64())
609+
if reorgedBlock == nil {
610+
return false, nil
611+
}
612+
598613
commitGasCounter.Dec(int64(reorgedBlock.GasUsed()))
599614
reorgedTxns := reorgedBlock.Transactions()
600615
var errorWithTxnIdx *ccc.ErrorWithTxnIdx
@@ -729,14 +744,14 @@ func (e retryableCommitError) Unwrap() error {
729744

730745
// commit runs any post-transaction state modifications, assembles the final block
731746
// and commits new work if consensus engine is running.
732-
func (w *worker) commit(reorging bool) (common.Hash, error) {
747+
func (w *worker) commit() (common.Hash, error) {
733748
sealDelay := time.Duration(0)
734749
defer func(t0 time.Time) {
735750
l2CommitTimer.Update(time.Since(t0) - sealDelay)
736751
}(time.Now())
737752

738753
w.updateSnapshot()
739-
if !w.isRunning() && !reorging {
754+
if !w.isRunning() && !w.current.reorging {
740755
return common.Hash{}, nil
741756
}
742757

@@ -813,7 +828,7 @@ func (w *worker) commit(reorging bool) (common.Hash, error) {
813828

814829
currentHeight := w.current.header.Number.Uint64()
815830
maxReorgDepth := uint64(w.config.CCCMaxWorkers + 1)
816-
if !reorging && currentHeight > maxReorgDepth {
831+
if !w.current.reorging && currentHeight > maxReorgDepth {
817832
ancestorHeight := currentHeight - maxReorgDepth
818833
ancestorHash := w.chain.GetHeaderByNumber(ancestorHeight).Hash()
819834
if rawdb.ReadBlockRowConsumption(w.chain.Database(), ancestorHash) == nil {
@@ -980,7 +995,7 @@ func (w *worker) handleReorg(trigger *reorgTrigger) error {
980995
return nil
981996
}
982997

983-
newBlockHash, err := w.tryCommitNewWork(time.Now(), parentHash, reorgReason)
998+
newBlockHash, err := w.tryCommitNewWork(time.Now(), parentHash, true, reorgReason)
984999
if err != nil {
9851000
return err
9861001
}
@@ -989,7 +1004,7 @@ func (w *worker) handleReorg(trigger *reorgTrigger) error {
9891004
if newBlockHash == (common.Hash{}) {
9901005
// force committing the new canonical head to trigger a reorg in blockchain
9911006
// otherwise we might ignore CCC errors from the new side chain since it is not canonical yet
992-
newBlockHash, err = w.commit(true)
1007+
newBlockHash, err = w.commit()
9931008
if err != nil {
9941009
return err
9951010
}

rollup/ccc/async_checker.go

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
173173
}
174174

175175
header := block.Header()
176-
header.GasUsed = 0
177-
gasPool := new(core.GasPool).AddGas(header.GasLimit)
178176
ccc.Reset()
179177

180178
accRc := new(types.RowConsumption)
@@ -184,7 +182,7 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
184182
}
185183

186184
var curRc *types.RowConsumption
187-
curRc, err = c.checkTxAndApply(parent, header, statedb, gasPool, tx, ccc)
185+
curRc, err = c.checkTx(parent, header, statedb, tx, ccc)
188186
if err != nil {
189187
err = &ErrorWithTxnIdx{
190188
TxIdx: uint(txIdx),
@@ -208,39 +206,14 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
208206
}
209207
}
210208

211-
func (c *AsyncChecker) checkTxAndApply(parent *types.Block, header *types.Header, state *state.StateDB, gasPool *core.GasPool, tx *types.Transaction, ccc *Checker) (*types.RowConsumption, error) {
212-
// don't commit the state during tracing for circuit capacity checker, otherwise we cannot revert.
213-
// and even if we don't commit the state, the `refund` value will still be correct, as explained in `CommitTransaction`
214-
commitStateAfterApply := false
215-
snap := state.Snapshot()
216-
217-
// 1. we have to check circuit capacity before `core.ApplyTransaction`,
218-
// because if the tx can be successfully executed but circuit capacity overflows, it will be inconvenient to revert.
219-
// 2. even if we don't commit to the state during the tracing (which means `clearJournalAndRefund` is not called during the tracing),
220-
// the `refund` value will still be correct, because:
221-
// 2.1 when starting handling the first tx, `state.refund` is 0 by default,
222-
// 2.2 after tracing, the state is either committed in `core.ApplyTransaction`, or reverted, so the `state.refund` can be cleared,
223-
// 2.3 when starting handling the following txs, `state.refund` comes as 0
209+
func (c *AsyncChecker) checkTx(parent *types.Block, header *types.Header, state *state.StateDB, tx *types.Transaction, ccc *Checker) (*types.RowConsumption, error) {
224210
trace, err := tracing.NewTracerWrapper().CreateTraceEnvAndGetBlockTrace(c.bc.Config(), c.bc, c.bc.Engine(), c.bc.Database(),
225-
state, parent.Header(), types.NewBlockWithHeader(header).WithBody([]*types.Transaction{tx}, nil), commitStateAfterApply)
226-
// `w.current.traceEnv.State` & `w.current.state` share a same pointer to the state, so only need to revert `w.current.state`
227-
// revert to snapshot for calling `core.ApplyMessage` again, (both `traceEnv.GetBlockTrace` & `core.ApplyTransaction` will call `core.ApplyMessage`)
228-
state.RevertToSnapshot(snap)
211+
state, parent.Header(), types.NewBlockWithHeader(header).WithBody([]*types.Transaction{tx}, nil), true)
229212
if err != nil {
230213
return nil, err
231214
}
232215

233-
rc, err := ccc.ApplyTransaction(trace)
234-
if err != nil {
235-
return rc, err
236-
}
237-
238-
_, err = core.ApplyTransaction(c.bc.Config(), c.bc, nil /* coinbase will default to chainConfig.Scroll.FeeVaultAddress */, gasPool,
239-
state, header, tx, &header.GasUsed, *c.bc.GetVMConfig())
240-
if err != nil {
241-
return nil, err
242-
}
243-
return rc, nil
216+
return ccc.ApplyTransaction(trace)
244217
}
245218

246219
// ScheduleError forces a block to error on a given transaction index

0 commit comments

Comments
 (0)