Skip to content

Commit 6471017

Browse files
Fjord: Implement max sequencer drift change to a constant (ethereum-optimism#10465)
* op-node/rollup: Add MaxSequencerDrift to ChainSpec * op-node/rollup,op-e2e: Use spec max seq drift instead of config * op-node/rollup: Showcase feature/fork separation pattern with seq drift change * op-node/driver: add origin selector Fjord test * op-node/rollup: refactor batch validation test prepare to allow for general modification of rollup config * op-node/rollup: add batch validation test * Update op-node/rollup/types.go Co-authored-by: Joshua Gutow <jgutow@oplabs.co> * op-node/rollup/derive: add Fjord span batch validation test --------- Co-authored-by: Joshua Gutow <jgutow@oplabs.co>
1 parent 7daf06d commit 6471017

File tree

10 files changed

+253
-96
lines changed

10 files changed

+253
-96
lines changed

op-e2e/actions/l2_sequencer_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) {
111111
sequencer.ActL1HeadSignal(t)
112112

113113
// Make blocks up till the sequencer drift is about to surpass, but keep the old L1 origin
114-
for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime <= origin.Time+sd.RollupCfg.MaxSequencerDrift {
114+
for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime <= origin.Time+sd.ChainSpec.MaxSequencerDrift(origin.Time) {
115115
sequencer.ActL2KeepL1Origin(t)
116116
makeL2BlockWithAliceTx()
117117
require.Equal(t, uint64(1), sequencer.SyncStatus().UnsafeL2.L1Origin.Number, "expected to keep old L1 origin")

op-e2e/e2eutils/setup.go

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type SetupData struct {
8080
L1Cfg *core.Genesis
8181
L2Cfg *core.Genesis
8282
RollupCfg *rollup.Config
83+
ChainSpec *rollup.ChainSpec
8384
DeploymentsL1 *genesis.L1Deployments
8485
}
8586

@@ -187,6 +188,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
187188
L1Cfg: l1Genesis,
188189
L2Cfg: l2Genesis,
189190
RollupCfg: rollupCfg,
191+
ChainSpec: rollup.NewChainSpec(rollupCfg),
190192
DeploymentsL1: l1Deployments,
191193
}
192194
}

op-node/rollup/chain_spec.go

+22
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ const (
2020
// TODO(#10428) Remove this parameter
2121
const SafeMaxRLPBytesPerChannel = maxRLPBytesPerChannelBedrock
2222

23+
// Fjord changes the max sequencer drift to a protocol constant. It was previously configurable via
24+
// the rollup config.
25+
// From Fjord, the max sequencer drift for a given block timestamp should be learned via the
26+
// ChainSpec instead of reading the rollup configuration field directly.
27+
const maxSequencerDriftFjord = 1800
28+
2329
type ChainSpec struct {
2430
config *Config
2531
}
@@ -55,3 +61,19 @@ func (s *ChainSpec) MaxRLPBytesPerChannel(t uint64) uint64 {
5561
}
5662
return maxRLPBytesPerChannelBedrock
5763
}
64+
65+
// IsFeatMaxSequencerDriftConstant specifies in which fork the max sequencer drift change to a
66+
// constant will be performed.
67+
func (s *ChainSpec) IsFeatMaxSequencerDriftConstant(t uint64) bool {
68+
return s.config.IsFjord(t)
69+
}
70+
71+
// MaxSequencerDrift returns the maximum sequencer drift for the given block timestamp. Until Fjord,
72+
// this was a rollup configuration parameter. Since Fjord, it is a constant, so its effective value
73+
// should always be queried via the ChainSpec.
74+
func (s *ChainSpec) MaxSequencerDrift(t uint64) uint64 {
75+
if s.IsFeatMaxSequencerDriftConstant(t) {
76+
return maxSequencerDriftFjord
77+
}
78+
return s.config.MaxSequencerDrift
79+
}

op-node/rollup/chain_spec_test.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var testConfig = Config{
5050
UsePlasma: false,
5151
}
5252

53-
func TestCanyonForkActivation(t *testing.T) {
53+
func TestChainSpec_CanyonForkActivation(t *testing.T) {
5454
c := NewChainSpec(&testConfig)
5555
tests := []struct {
5656
name string
@@ -74,7 +74,7 @@ func TestCanyonForkActivation(t *testing.T) {
7474
}
7575
}
7676

77-
func TestMaxChannelBankSize(t *testing.T) {
77+
func TestChainSpec_MaxChannelBankSize(t *testing.T) {
7878
c := NewChainSpec(&testConfig)
7979
tests := []struct {
8080
name string
@@ -97,7 +97,7 @@ func TestMaxChannelBankSize(t *testing.T) {
9797
}
9898
}
9999

100-
func TestMaxRLPBytesPerChannel(t *testing.T) {
100+
func TestChainSpec_MaxRLPBytesPerChannel(t *testing.T) {
101101
c := NewChainSpec(&testConfig)
102102
tests := []struct {
103103
name string
@@ -119,3 +119,26 @@ func TestMaxRLPBytesPerChannel(t *testing.T) {
119119
})
120120
}
121121
}
122+
123+
func TestChainSpec_MaxSequencerDrift(t *testing.T) {
124+
c := NewChainSpec(&testConfig)
125+
tests := []struct {
126+
name string
127+
blockNum uint64
128+
expected uint64
129+
description string
130+
}{
131+
{"Genesis", 0, testConfig.MaxSequencerDrift, "Before Fjord activation, should use rollup config value"},
132+
{"FjordTimeMinusOne", 49, testConfig.MaxSequencerDrift, "Just before Fjord, should still use rollup config value"},
133+
{"FjordTime", 50, maxSequencerDriftFjord, "At Fjord activation, should switch to Fjord constant"},
134+
{"FjordTimePlusOne", 51, maxSequencerDriftFjord, "After Fjord activation, should use Fjord constant"},
135+
{"NextForkTime", 60, maxSequencerDriftFjord, "Well after Fjord, should continue to use Fjord constant"},
136+
}
137+
138+
for _, tt := range tests {
139+
t.Run(tt.name, func(t *testing.T) {
140+
result := c.MaxSequencerDrift(tt.blockNum)
141+
require.Equal(t, tt.expected, result, tt.description)
142+
})
143+
}
144+
}

op-node/rollup/derive/batches.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ const (
3232
// The first entry of the l1Blocks should match the origin of the l2SafeHead. One or more consecutive l1Blocks should be provided.
3333
// In case of only a single L1 block, the decision whether a batch is valid may have to stay undecided.
3434
func CheckBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1BlockRef,
35-
l2SafeHead eth.L2BlockRef, batch *BatchWithL1InclusionBlock, l2Fetcher SafeBlockFetcher) BatchValidity {
35+
l2SafeHead eth.L2BlockRef, batch *BatchWithL1InclusionBlock, l2Fetcher SafeBlockFetcher,
36+
) BatchValidity {
3637
switch batch.Batch.GetBatchType() {
3738
case SingularBatchType:
3839
singularBatch, ok := batch.Batch.(*SingularBatch)
@@ -122,8 +123,9 @@ func checkSingularBatch(cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1Blo
122123
return BatchDrop
123124
}
124125

126+
spec := rollup.NewChainSpec(cfg)
125127
// Check if we ran out of sequencer time drift
126-
if max := batchOrigin.Time + cfg.MaxSequencerDrift; batch.Timestamp > max {
128+
if max := batchOrigin.Time + spec.MaxSequencerDrift(batchOrigin.Time); batch.Timestamp > max {
127129
if len(batch.Transactions) == 0 {
128130
// If the sequencer is co-operating by producing an empty batch,
129131
// then allow the batch if it was the right thing to do to maintain the L2 time >= L1 time invariant.
@@ -166,7 +168,8 @@ func checkSingularBatch(cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1Blo
166168

167169
// checkSpanBatch implements SpanBatch validation rule.
168170
func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1BlockRef, l2SafeHead eth.L2BlockRef,
169-
batch *SpanBatch, l1InclusionBlock eth.L1BlockRef, l2Fetcher SafeBlockFetcher) BatchValidity {
171+
batch *SpanBatch, l1InclusionBlock eth.L1BlockRef, l2Fetcher SafeBlockFetcher,
172+
) BatchValidity {
170173
// add details to the log
171174
log = batch.LogContext(log)
172175

@@ -266,10 +269,7 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
266269
}
267270

268271
originIdx := 0
269-
originAdvanced := false
270-
if startEpochNum == parentBlock.L1Origin.Number+1 {
271-
originAdvanced = true
272-
}
272+
originAdvanced := startEpochNum == parentBlock.L1Origin.Number+1
273273

274274
for i := 0; i < batch.GetBlockCount(); i++ {
275275
if batch.GetBlockTimestamp(i) <= l2SafeHead.Time {
@@ -282,7 +282,6 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
282282
originIdx = j
283283
break
284284
}
285-
286285
}
287286
if i > 0 {
288287
originAdvanced = false
@@ -296,8 +295,9 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
296295
return BatchDrop
297296
}
298297

298+
spec := rollup.NewChainSpec(cfg)
299299
// Check if we ran out of sequencer time drift
300-
if max := l1Origin.Time + cfg.MaxSequencerDrift; blockTimestamp > max {
300+
if max := l1Origin.Time + spec.MaxSequencerDrift(l1Origin.Time); blockTimestamp > max {
301301
if len(batch.GetBlockTransactions(i)) == 0 {
302302
// If the sequencer is co-operating by producing an empty batch,
303303
// then allow the batch if it was the right thing to do to maintain the L2 time >= L1 time invariant.

0 commit comments

Comments
 (0)