Skip to content

Commit 3b164a8

Browse files
Inphiajsutton
andauthored
op-e2e: Add message expiry action test (ethereum-optimism#14346)
* op-e2e: Add message expiry action test * update test fixture * fix comment * reduce messag expiry time * Add DSL option to repeat block creation until a timestamp is reached * restore test skip --------- Co-authored-by: Adrian Sutton <[email protected]>
1 parent 12528f4 commit 3b164a8

File tree

7 files changed

+129
-28
lines changed

7 files changed

+129
-28
lines changed

op-chain-ops/genesis/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ type DevDeployConfig struct {
8383
// FundDevAccounts configures whether to fund the dev accounts.
8484
// This should only be used during devnet deployments.
8585
FundDevAccounts bool `json:"fundDevAccounts"`
86+
// OverrideMessageExpiryTime configures the message expiry time of interop messages.
87+
// This should only be used during devnet deployments.
88+
OverrideMessageExpiryTime uint64 `json:"overrideMessageExpiryTime"`
8689
}
8790

8891
type L2GenesisBlockDeployConfig struct {

op-chain-ops/genesis/testdata/test-deploy-config-full.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
7575
"faultGameSplitDepth": 0,
7676
"faultGameWithdrawalDelay": 604800,
77+
"overrideMessageExpiryTime": 0,
7778
"preimageOracleMinProposalSize": 1800000,
7879
"preimageOracleChallengePeriod": 86400,
7980
"systemConfigStartBlock": 0,

op-chain-ops/interopgen/deploy.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ func CompleteL2(l2Host *script.Host, cfg *L2Config, l1Block *types.Block, deploy
329329
if err != nil {
330330
return nil, fmt.Errorf("failed to build L2 rollup config: %w", err)
331331
}
332+
if cfg.OverrideMessageExpiryTime != 0 {
333+
rollupCfg.OverrideMessageExpiryTimeInterop = cfg.OverrideMessageExpiryTime
334+
}
332335
return &L2Output{
333336
Genesis: l2Genesis,
334337
RollupCfg: rollupCfg,

op-chain-ops/interopgen/recipe.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ import (
1515
)
1616

1717
type InteropDevRecipe struct {
18-
L1ChainID uint64
19-
L2ChainIDs []uint64
20-
GenesisTimestamp uint64
18+
L1ChainID uint64
19+
L2ChainIDs []uint64
20+
GenesisTimestamp uint64
21+
MessageExpiryTime uint64
2122
}
2223

2324
func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) {
@@ -93,7 +94,7 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error)
9394
L2s: make(map[string]*L2Config),
9495
}
9596
for _, l2ChainID := range r.L2ChainIDs {
96-
l2Cfg, err := InteropL2DevConfig(r.L1ChainID, l2ChainID, addrs)
97+
l2Cfg, err := InteropL2DevConfig(r.L1ChainID, l2ChainID, addrs, r.MessageExpiryTime)
9798
if err != nil {
9899
return nil, fmt.Errorf("failed to generate L2 config for chain %d: %w", l2ChainID, err)
99100
}
@@ -129,7 +130,7 @@ func prefundL2Accounts(l1Cfg *L1Config, l2Cfg *L2Config, addrs devkeys.Addresses
129130
return nil
130131
}
131132

132-
func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (*L2Config, error) {
133+
func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses, messageExpiryTime uint64) (*L2Config, error) {
133134
// Padded chain ID, hex encoded, prefixed with 0xff like inboxes, then 0x02 to signify devnet.
134135
batchInboxAddress := common.HexToAddress(fmt.Sprintf("0xff02%016x", l2ChainID))
135136
chainOps := devkeys.ChainOperatorKeys(new(big.Int).SetUint64(l2ChainID))
@@ -186,7 +187,8 @@ func InteropL2DevConfig(l1ChainID, l2ChainID uint64, addrs devkeys.Addresses) (*
186187
SystemConfigOwner: systemConfigOwner,
187188
L2InitializationConfig: genesis.L2InitializationConfig{
188189
DevDeployConfig: genesis.DevDeployConfig{
189-
FundDevAccounts: true,
190+
FundDevAccounts: true,
191+
OverrideMessageExpiryTime: messageExpiryTime,
190192
},
191193
L2GenesisBlockDeployConfig: genesis.L2GenesisBlockDeployConfig{
192194
L2GenesisBlockGasLimit: 60_000_000,

op-e2e/actions/interop/dsl/dsl.go

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ type TransactionCreator func(chain *Chain) (*types.Transaction, common.Address)
120120
type AddL2BlockOpts struct {
121121
BlockIsNotCrossUnsafe bool
122122
TransactionCreators []TransactionCreator
123+
UntilTimestamp uint64
123124
}
124125

125126
func WithL2BlockTransactions(mkTxs ...TransactionCreator) func(*AddL2BlockOpts) {
@@ -134,31 +135,42 @@ func WithL1BlockCrossUnsafe() func(*AddL2BlockOpts) {
134135
}
135136
}
136137

138+
func WithL2BlocksUntilTimestamp(timestamp uint64) func(*AddL2BlockOpts) {
139+
return func(o *AddL2BlockOpts) {
140+
o.UntilTimestamp = timestamp
141+
}
142+
}
143+
137144
// AddL2Block adds a new unsafe block to the specified chain and fully processes it in the supervisor
138145
func (d *InteropDSL) AddL2Block(chain *Chain, optionalArgs ...func(*AddL2BlockOpts)) {
139146
opts := AddL2BlockOpts{}
140147
for _, arg := range optionalArgs {
141148
arg(&opts)
142149
}
143-
priorSyncStatus := chain.Sequencer.SyncStatus()
144-
chain.Sequencer.ActL2StartBlock(d.t)
145-
for _, creator := range opts.TransactionCreators {
146-
tx, from := creator(chain)
147-
err := chain.SequencerEngine.EngineApi.IncludeTx(tx, from)
148-
require.NoError(d.t, err)
149-
}
150-
chain.Sequencer.ActL2EndBlock(d.t)
151-
chain.Sequencer.SyncSupervisor(d.t)
152-
d.Actors.Supervisor.ProcessFull(d.t)
153-
chain.Sequencer.ActL2PipelineFull(d.t)
154-
155-
status := chain.Sequencer.SyncStatus()
156-
expectedBlockNum := priorSyncStatus.UnsafeL2.Number + 1
157-
require.Equal(d.t, expectedBlockNum, status.UnsafeL2.Number, "Unsafe head did not advance")
158-
if opts.BlockIsNotCrossUnsafe {
159-
require.Equal(d.t, priorSyncStatus.CrossUnsafeL2.Number, status.CrossUnsafeL2.Number, "CrossUnsafe head advanced unexpectedly")
160-
} else {
161-
require.Equal(d.t, expectedBlockNum, status.CrossUnsafeL2.Number, "CrossUnsafe head did not advance")
150+
for opts.UntilTimestamp == 0 || chain.Sequencer.L2Unsafe().Time <= opts.UntilTimestamp {
151+
priorSyncStatus := chain.Sequencer.SyncStatus()
152+
chain.Sequencer.ActL2StartBlock(d.t)
153+
for _, creator := range opts.TransactionCreators {
154+
tx, from := creator(chain)
155+
err := chain.SequencerEngine.EngineApi.IncludeTx(tx, from)
156+
require.NoError(d.t, err)
157+
}
158+
chain.Sequencer.ActL2EndBlock(d.t)
159+
chain.Sequencer.SyncSupervisor(d.t)
160+
d.Actors.Supervisor.ProcessFull(d.t)
161+
chain.Sequencer.ActL2PipelineFull(d.t)
162+
163+
status := chain.Sequencer.SyncStatus()
164+
expectedBlockNum := priorSyncStatus.UnsafeL2.Number + 1
165+
require.Equal(d.t, expectedBlockNum, status.UnsafeL2.Number, "Unsafe head did not advance")
166+
if opts.BlockIsNotCrossUnsafe {
167+
require.Equal(d.t, priorSyncStatus.CrossUnsafeL2.Number, status.CrossUnsafeL2.Number, "CrossUnsafe head advanced unexpectedly")
168+
} else {
169+
require.Equal(d.t, expectedBlockNum, status.CrossUnsafeL2.Number, "CrossUnsafe head did not advance")
170+
}
171+
if opts.UntilTimestamp == 0 {
172+
break
173+
}
162174
}
163175
}
164176

op-e2e/actions/interop/dsl/interop.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,19 @@ type InteropActors struct {
6868
ChainB *Chain
6969
}
7070

71+
// messageExpiryTime is the time in seconds that a message will be valid for on the L2 chain.
72+
// At a 2 second block time, this should be small enough to cover all events buffered in the supervisor event queue.
73+
const messageExpiryTime = 120 // 2 minutes
74+
7175
// SetupInterop creates an InteropSetup to instantiate actors on, with 2 L2 chains.
7276
func SetupInterop(t helpers.Testing) *InteropSetup {
7377
logger := testlog.Logger(t, log.LevelDebug)
7478

7579
recipe := interopgen.InteropDevRecipe{
76-
L1ChainID: 900100,
77-
L2ChainIDs: []uint64{900200, 900201},
78-
GenesisTimestamp: uint64(time.Now().Unix() + 3),
80+
L1ChainID: 900100,
81+
L2ChainIDs: []uint64{900200, 900201},
82+
GenesisTimestamp: uint64(time.Now().Unix() + 3),
83+
MessageExpiryTime: messageExpiryTime,
7984
}
8085
hdWallet, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
8186
require.NoError(t, err)

op-e2e/actions/interop/proofs_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,81 @@ func TestInteropFaultProofs_CascadeInvalidBlock(gt *testing.T) {
452452
runFppAndChallengerTests(gt, system, tests)
453453
}
454454

455+
func TestInteropFaultProofs_MessageExpiry(gt *testing.T) {
456+
t := helpers.NewDefaultTesting(gt)
457+
// TODO(#14234): Check message expiry in op-supervisor
458+
t.Skip("Message expiry not yet implemented")
459+
460+
system := dsl.NewInteropDSL(t)
461+
462+
actors := system.Actors
463+
alice := system.CreateUser()
464+
emitterContract := dsl.NewEmitterContract(t)
465+
system.AddL2Block(actors.ChainA, dsl.WithL2BlockTransactions(
466+
emitterContract.Deploy(alice),
467+
))
468+
system.AddL2Block(actors.ChainA, dsl.WithL2BlockTransactions(
469+
emitterContract.EmitMessage(alice, "test message"),
470+
))
471+
emitTx := emitterContract.LastEmittedMessage()
472+
473+
// Bring ChainB to the same height and timestamp
474+
system.AddL2Block(actors.ChainB, dsl.WithL2BlocksUntilTimestamp(actors.ChainA.Sequencer.L2Unsafe().Time))
475+
system.SubmitBatchData()
476+
477+
// Advance the chain until the init msg expires
478+
msgExpiryTime := actors.ChainA.RollupCfg.GetMessageExpiryTimeInterop()
479+
end := emitTx.Identifier().Timestamp.Uint64() + msgExpiryTime
480+
system.AddL2Block(actors.ChainA, dsl.WithL2BlocksUntilTimestamp(end))
481+
system.AddL2Block(actors.ChainB, dsl.WithL2BlocksUntilTimestamp(end))
482+
system.SubmitBatchData()
483+
484+
system.AddL2Block(actors.ChainB, func(opts *dsl.AddL2BlockOpts) {
485+
opts.TransactionCreators = []dsl.TransactionCreator{system.InboxContract.Execute(alice, emitTx.Identifier(), emitTx.MessagePayload())}
486+
opts.BlockIsNotCrossUnsafe = true
487+
})
488+
system.AddL2Block(actors.ChainA)
489+
490+
system.SubmitBatchData(func(opts *dsl.SubmitBatchDataOpts) {
491+
opts.SkipCrossSafeUpdate = true
492+
})
493+
execTx := system.InboxContract.LastTransaction()
494+
execTx.CheckIncluded()
495+
496+
endTimestamp := actors.ChainB.Sequencer.L2Unsafe().Time
497+
startTimestamp := endTimestamp - 1
498+
optimisticEnd := system.Outputs.SuperRoot(endTimestamp)
499+
500+
preConsolidation := system.Outputs.TransitionState(startTimestamp, 1023,
501+
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainA, endTimestamp),
502+
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainB, endTimestamp),
503+
).Marshal()
504+
505+
// Induce block replacement
506+
system.ProcessCrossSafe()
507+
// assert that the invalid message txs were reorged out
508+
execTx.CheckNotIncluded()
509+
crossSafeEnd := system.Outputs.SuperRoot(endTimestamp)
510+
511+
tests := []*transitionTest{
512+
{
513+
name: "Consolidate-ExpectInvalidPendingBlock",
514+
agreedClaim: preConsolidation,
515+
disputedClaim: optimisticEnd.Marshal(),
516+
disputedTraceIndex: 1023,
517+
expectValid: false,
518+
},
519+
{
520+
name: "Consolidate-ReplaceInvalidBlocks",
521+
agreedClaim: preConsolidation,
522+
disputedClaim: crossSafeEnd.Marshal(),
523+
disputedTraceIndex: 1023,
524+
expectValid: true,
525+
},
526+
}
527+
runFppAndChallengerTests(gt, system, tests)
528+
}
529+
455530
func TestInteropFaultProofsInvalidBlock(gt *testing.T) {
456531
t := helpers.NewDefaultTesting(gt)
457532

0 commit comments

Comments
 (0)