Commit 66f9e8e8 authored by Joshua Gutow's avatar Joshua Gutow

op-batcher: Properly drain state on L2 reorgs

When a L2 reorg occurs the batcher should close out the pending channel
and submit everything that it has before clearing the channel builder.
The reason for this is that even though it may be submitting incorrect
data it should not leave dangling channels. After the pending state is
drained it can begin tracking the new L2 chain.
parent 60b30032
...@@ -187,13 +187,15 @@ func (l *BatchSubmitter) Stop(ctx context.Context) error { ...@@ -187,13 +187,15 @@ func (l *BatchSubmitter) Stop(ctx context.Context) error {
// 2. Check if the sync status is valid or if we are all the way up to date // 2. Check if the sync status is valid or if we are all the way up to date
// 3. Check if it needs to initialize state OR it is lagging (todo: lagging just means race condition?) // 3. Check if it needs to initialize state OR it is lagging (todo: lagging just means race condition?)
// 4. Load all new blocks into the local state. // 4. Load all new blocks into the local state.
func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) { // If there is a reorg, it will reset the last stored block but not clear the internal state so
// the state can be flushed to L1.
func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) error {
start, end, err := l.calculateL2BlockRangeToStore(ctx) start, end, err := l.calculateL2BlockRangeToStore(ctx)
if err != nil { if err != nil {
l.log.Warn("Error calculating L2 block range", "err", err) l.log.Warn("Error calculating L2 block range", "err", err)
return return err
} else if start.Number >= end.Number { } else if start.Number >= end.Number {
return return errors.New("start number is >= end number")
} }
var latestBlock *types.Block var latestBlock *types.Block
...@@ -202,12 +204,11 @@ func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) { ...@@ -202,12 +204,11 @@ func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) {
block, err := l.loadBlockIntoState(ctx, i) block, err := l.loadBlockIntoState(ctx, i)
if errors.Is(err, ErrReorg) { if errors.Is(err, ErrReorg) {
l.log.Warn("Found L2 reorg", "block_number", i) l.log.Warn("Found L2 reorg", "block_number", i)
l.state.Clear()
l.lastStoredBlock = eth.BlockID{} l.lastStoredBlock = eth.BlockID{}
return return err
} else if err != nil { } else if err != nil {
l.log.Warn("failed to load block into state", "err", err) l.log.Warn("failed to load block into state", "err", err)
return return err
} }
l.lastStoredBlock = eth.ToBlockID(block) l.lastStoredBlock = eth.ToBlockID(block)
latestBlock = block latestBlock = block
...@@ -216,10 +217,11 @@ func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) { ...@@ -216,10 +217,11 @@ func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) {
l2ref, err := derive.L2BlockToBlockRef(latestBlock, &l.Rollup.Genesis) l2ref, err := derive.L2BlockToBlockRef(latestBlock, &l.Rollup.Genesis)
if err != nil { if err != nil {
l.log.Warn("Invalid L2 block loaded into state", "err", err) l.log.Warn("Invalid L2 block loaded into state", "err", err)
return return err
} }
l.metr.RecordL2BlocksLoaded(l2ref) l.metr.RecordL2BlocksLoaded(l2ref)
return nil
} }
// loadBlockIntoState fetches & stores a single block into `state`. It returns the block it loaded. // loadBlockIntoState fetches & stores a single block into `state`. It returns the block it loaded.
...@@ -294,7 +296,15 @@ func (l *BatchSubmitter) loop() { ...@@ -294,7 +296,15 @@ func (l *BatchSubmitter) loop() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
l.loadBlocksIntoState(l.shutdownCtx) if err := l.loadBlocksIntoState(l.shutdownCtx); errors.Is(err, ErrReorg) {
err := l.state.Close()
if err != nil {
l.log.Error("error closing the channel manager to handle a L2 reorg", "err", err)
}
l.publishStateToL1(queue, receiptsCh, true)
l.state.Clear()
continue
}
l.publishStateToL1(queue, receiptsCh, false) l.publishStateToL1(queue, receiptsCh, false)
case r := <-receiptsCh: case r := <-receiptsCh:
l.handleReceipt(r) l.handleReceipt(r)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment