Skip to content

Commit cd28d29

Browse files
authored
op-acceptance-tests: Cache and canonicalize L2EL payloads after gap fill (#17675)
* op-acceptance-tests: Cache and canonicalize L2EL payloads after gap fill * Clarify args for retrying * better var name
1 parent 4d5dfc2 commit cd28d29

File tree

5 files changed

+125
-5
lines changed

5 files changed

+125
-5
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package elsync
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
7+
"github.com/ethereum-optimism/optimism/op-devstack/presets"
8+
"github.com/ethereum-optimism/optimism/op-service/eth"
9+
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
10+
)
11+
12+
func TestELSyncAfterInitialELSync(gt *testing.T) {
13+
t := devtest.SerialT(gt)
14+
sys := presets.NewSingleChainMultiNodeWithoutCheck(t)
15+
require := t.Require()
16+
17+
sys.L2CL.Advanced(types.LocalUnsafe, 7, 30)
18+
19+
// batcher down so safe not advanced
20+
require.Equal(uint64(0), sys.L2CL.HeadBlockRef(types.LocalSafe).Number)
21+
require.Equal(uint64(0), sys.L2CLB.HeadBlockRef(types.LocalSafe).Number)
22+
// verifier not advanced unsafe head
23+
require.Equal(uint64(0), sys.L2CLB.HeadBlockRef(types.LocalUnsafe).Number)
24+
25+
// Finish EL sync by supplying the first block
26+
// EL Sync finished because underlying EL has states to validate the payload for block 1
27+
sys.L2CLB.SignalTarget(sys.L2EL, 1)
28+
29+
// Note: Below non-canonical payload caching behavior was only observed after the initial EL Sync has finished
30+
31+
// Send payloads for block 3, 4, 5, 7 which will make non-canonical blocks, block 2 missed
32+
// Non-canonical payloads will be buffered at the L2EL
33+
// Order does not matter
34+
for _, target := range []uint64{5, 3, 4, 7} {
35+
sys.L2CLB.SignalTarget(sys.L2EL, target)
36+
// Canonical unsafe head never advances because of the gap
37+
require.Equal(uint64(1), sys.L2ELB.BlockRefByLabel(eth.Unsafe).Number)
38+
}
39+
40+
// Send missing gap, payload 2
41+
sys.L2CLB.SignalTarget(sys.L2EL, 2)
42+
43+
retries := 2
44+
// Gap filled and payload 2, 3, 4, 5 became canonical. Payload 7 is still non canonical
45+
sys.L2ELB.Reached(eth.Unsafe, 5, retries)
46+
47+
// Send missing gap, payload 6
48+
sys.L2CLB.SignalTarget(sys.L2EL, 6)
49+
50+
// Gap filled and block 6, 7 became canonical
51+
sys.L2ELB.Reached(eth.Unsafe, 7, retries)
52+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package elsync
2+
3+
import (
4+
"testing"
5+
6+
bss "github.com/ethereum-optimism/optimism/op-batcher/batcher"
7+
"github.com/ethereum-optimism/optimism/op-devstack/compat"
8+
"github.com/ethereum-optimism/optimism/op-devstack/presets"
9+
"github.com/ethereum-optimism/optimism/op-devstack/stack"
10+
"github.com/ethereum-optimism/optimism/op-devstack/sysgo"
11+
)
12+
13+
func TestMain(m *testing.M) {
14+
// No ELP2P, CLP2P to control the supply of unsafe payload to the CL
15+
presets.DoMain(m, presets.WithSingleChainMultiNodeWithoutP2P(),
16+
presets.WithExecutionLayerSyncOnVerifiers(),
17+
presets.WithCompatibleTypes(compat.SysGo),
18+
stack.MakeCommon(sysgo.WithBatcherOption(func(id stack.L2BatcherID, cfg *bss.CLIConfig) {
19+
// For stopping derivation, not to advance safe heads
20+
cfg.Stopped = true
21+
})),
22+
)
23+
}

op-devstack/dsl/l2_el.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,23 @@ func (el *L2ELNode) NotAdvancedFn(label eth.BlockLabel) CheckFunc {
9090
}
9191
}
9292

93+
func (el *L2ELNode) ReachedFn(label eth.BlockLabel, target uint64, attempts int) CheckFunc {
94+
return func() error {
95+
logger := el.log.With("id", el.inner.ID(), "chain", el.ChainID(), "label", label, "target", target)
96+
logger.Info("Expecting L2EL to reach")
97+
return retry.Do0(el.ctx, attempts, &retry.FixedStrategy{Dur: 2 * time.Second},
98+
func() error {
99+
head := el.BlockRefByLabel(label)
100+
if head.Number >= target {
101+
logger.Info("L2EL advanced", "target", target)
102+
return nil
103+
}
104+
logger.Info("L2EL sync status", "current", head.Number)
105+
return fmt.Errorf("expected head to advance: %s", label)
106+
})
107+
}
108+
}
109+
93110
func (el *L2ELNode) BlockRefByNumber(num uint64) eth.L2BlockRef {
94111
ctx, cancel := context.WithTimeout(el.ctx, DefaultTimeout)
95112
defer cancel()
@@ -135,6 +152,10 @@ func (el *L2ELNode) Advanced(label eth.BlockLabel, block uint64) {
135152
el.require.NoError(el.AdvancedFn(label, block)())
136153
}
137154

155+
func (el *L2ELNode) Reached(label eth.BlockLabel, block uint64, attempts int) {
156+
el.require.NoError(el.ReachedFn(label, block, attempts)())
157+
}
158+
138159
func (el *L2ELNode) NotAdvanced(label eth.BlockLabel) {
139160
el.require.NoError(el.NotAdvancedFn(label)())
140161
}

op-devstack/presets/singlechain_multinode.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ func WithSingleChainMultiNode() stack.CommonOption {
2222
}
2323

2424
func NewSingleChainMultiNode(t devtest.T) *SingleChainMultiNode {
25+
preset := NewSingleChainMultiNodeWithoutCheck(t)
26+
// Ensure the follower node is in sync with the sequencer before starting tests
27+
dsl.CheckAll(t,
28+
preset.L2CLB.MatchedFn(preset.L2CL, types.CrossSafe, 30),
29+
preset.L2CLB.MatchedFn(preset.L2CL, types.LocalUnsafe, 30),
30+
)
31+
return preset
32+
}
33+
34+
func NewSingleChainMultiNodeWithoutCheck(t devtest.T) *SingleChainMultiNode {
2535
system := shim.NewSystem(t)
2636
orch := Orchestrator()
2737
orch.Hydrate(system)
@@ -41,10 +51,9 @@ func NewSingleChainMultiNode(t devtest.T) *SingleChainMultiNode {
4151
L2ELB: dsl.NewL2ELNode(verifierEL, orch.ControlPlane()),
4252
L2CLB: dsl.NewL2CLNode(verifierCL, orch.ControlPlane()),
4353
}
44-
// Ensure the follower node is in sync with the sequencer before starting tests
45-
dsl.CheckAll(t,
46-
preset.L2CLB.MatchedFn(preset.L2CL, types.CrossSafe, 30),
47-
preset.L2CLB.MatchedFn(preset.L2CL, types.LocalUnsafe, 30),
48-
)
4954
return preset
5055
}
56+
57+
func WithSingleChainMultiNodeWithoutP2P() stack.CommonOption {
58+
return stack.MakeCommon(sysgo.DefaultSingleChainMultiNodeSystemWithoutP2P(&sysgo.DefaultSingleChainMultiNodeSystemIDs{}))
59+
}

op-devstack/sysgo/system_singlechain_multinode.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,18 @@ func DefaultSingleChainMultiNodeSystem(dest *DefaultSingleChainMultiNodeSystemID
3939
}))
4040
return opt
4141
}
42+
43+
func DefaultSingleChainMultiNodeSystemWithoutP2P(dest *DefaultSingleChainMultiNodeSystemIDs) stack.Option[*Orchestrator] {
44+
ids := NewDefaultSingleChainMultiNodeSystemIDs(DefaultL1ID, DefaultL2AID)
45+
46+
opt := stack.Combine[*Orchestrator]()
47+
opt.Add(DefaultMinimalSystem(&dest.DefaultMinimalSystemIDs))
48+
49+
opt.Add(WithL2ELNode(ids.L2ELB))
50+
opt.Add(WithL2CLNode(ids.L2CLB, ids.L1CL, ids.L1EL, ids.L2ELB))
51+
52+
opt.Add(stack.Finally(func(orch *Orchestrator) {
53+
*dest = ids
54+
}))
55+
return opt
56+
}

0 commit comments

Comments
 (0)