1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package driver
import (
"fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/rollup/engine"
"github.com/ethereum-optimism/optimism/op-node/rollup/event"
)
// ProgramDeriver expresses how engine and derivation events are
// translated and monitored to execute the pure L1 to L2 state transition.
//
// The ProgramDeriver stops at the target block number or with an error result.
type ProgramDeriver struct {
logger log.Logger
Emitter event.Emitter
closing bool
result error
targetBlockNum uint64
}
func (d *ProgramDeriver) Closing() bool {
return d.closing
}
func (d *ProgramDeriver) Result() error {
return d.result
}
func (d *ProgramDeriver) OnEvent(ev event.Event) bool {
switch x := ev.(type) {
case engine.EngineResetConfirmedEvent:
d.Emitter.Emit(derive.ConfirmPipelineResetEvent{})
// After initial reset we can request the pending-safe block,
// where attributes will be generated on top of.
d.Emitter.Emit(engine.PendingSafeRequestEvent{})
case engine.PendingSafeUpdateEvent:
d.Emitter.Emit(derive.PipelineStepEvent{PendingSafe: x.PendingSafe})
case derive.DeriverMoreEvent:
d.Emitter.Emit(engine.PendingSafeRequestEvent{})
case derive.DerivedAttributesEvent:
// Allow new attributes to be generated.
// We will process the current attributes synchronously,
// triggering a single PendingSafeUpdateEvent or InvalidPayloadAttributesEvent,
// to continue derivation from.
d.Emitter.Emit(derive.ConfirmReceivedAttributesEvent{})
// No need to queue the attributes, since there is no unsafe chain to consolidate against,
// and no temporary-error retry to perform on block processing.
d.Emitter.Emit(engine.BuildStartEvent{Attributes: x.Attributes})
case engine.InvalidPayloadAttributesEvent:
// If a set of attributes was invalid, then we drop the attributes,
// and continue with the next.
d.Emitter.Emit(engine.PendingSafeRequestEvent{})
case engine.ForkchoiceUpdateEvent:
if x.SafeL2Head.Number >= d.targetBlockNum {
d.logger.Info("Derivation complete: reached L2 block", "head", x.SafeL2Head)
d.closing = true
}
case derive.DeriverIdleEvent:
// Not enough data to reach target
d.closing = true
d.logger.Info("Derivation complete: no further data to process")
case rollup.ResetEvent:
d.closing = true
d.result = fmt.Errorf("unexpected reset error: %w", x.Err)
case rollup.L1TemporaryErrorEvent:
d.closing = true
d.result = fmt.Errorf("unexpected L1 error: %w", x.Err)
case rollup.EngineTemporaryErrorEvent:
// (Legacy case): While most temporary errors are due to requests for external data failing which can't happen,
// they may also be returned due to other events like channels timing out so need to be handled
d.logger.Warn("Temporary error in derivation", "err", x.Err)
d.Emitter.Emit(engine.PendingSafeRequestEvent{})
case rollup.CriticalErrorEvent:
d.closing = true
d.result = x.Err
default:
// Other events can be ignored safely.
// They are broadcast, but only consumed by the other derivers,
// or do not affect the state-transition.
return false
}
return true
}