From 6ca2be19610844dea7cd7a1dcf04ec74704c250b Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 18 Sep 2024 18:21:38 -0400 Subject: [PATCH 1/3] feat(op-e2e): Open channel closed after sequence window expiry test Adds a test to the proof actions that proves a block at the safe head after the batcher has opened a channel, allowed the sequence window to expire, and then attempted to close their open channel after the fact. --- .../proofs/sequence_window_expiry_test.go | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/op-e2e/actions/proofs/sequence_window_expiry_test.go b/op-e2e/actions/proofs/sequence_window_expiry_test.go index 9a7a281cdcc0..e27169746354 100644 --- a/op-e2e/actions/proofs/sequence_window_expiry_test.go +++ b/op-e2e/actions/proofs/sequence_window_expiry_test.go @@ -48,6 +48,79 @@ func runSequenceWindowExpireTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64()/2, testCfg.CheckResult, testCfg.InputParams...) } +// Runs a that proves a block in a chain where the batcher opens a channel, the sequence window expires, and then the +// batcher attempts to close the channel afterwards. +func runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test(gt *testing.T, testCfg *helpers.TestCfg[any]) { + t := actionsHelpers.NewDefaultTesting(gt) + tp := helpers.NewTestParams() + env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg()) + + // Mine 2 empty blocks on L2. + for i := 0; i < 2; i++ { + env.Sequencer.ActL2StartBlock(t) + env.Sequencer.ActL2EndBlock(t) + } + + // Open the channel on L1. + env.Batcher.ActL2BatchBuffer(t) + env.Batcher.ActL2BatchSubmit(t) + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + + // Finalize the block with the first channel frame on L1. + env.Miner.ActL1SafeNext(t) + env.Miner.ActL1FinalizeNext(t) + + // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + // Ensure the safe head is still 0. + l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock() + require.EqualValues(t, 0, l2SafeHead.Number.Uint64()) + + // Expire the sequence window by building `SequenceWindow + 1` empty blocks on L1. + for i := 0; i < int(tp.SequencerWindowSize)+1; i++ { + env.Alice.L1.ActResetTxOpts(t) + env.Alice.ActDeposit(t) + + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTx(env.Alice.Address())(t) + env.Miner.ActL1EndBlock(t) + + env.Miner.ActL1SafeNext(t) + env.Miner.ActL1FinalizeNext(t) + } + + // Instruct the batcher the channel on L1, after the sequence window + channel timeout has elapsed. + env.Batcher.ActL2BatchBuffer(t) + env.Batcher.ActL2ChannelClose(t) + env.Batcher.ActL2BatchSubmit(t) + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + + // Finalize the block with the second channel frame on L1. + env.Miner.ActL1SafeNext(t) + env.Miner.ActL1FinalizeNext(t) + + // Ensure the safe head is still 0. + l2SafeHead = env.Engine.L2Chain().CurrentSafeBlock() + require.EqualValues(t, 0, l2SafeHead.Number.Uint64()) + + // Ask the sequencer to derive the deposit-only L2 chain. + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + // Ensure the safe head advanced forcefully. + l2SafeHead = env.Engine.L2Chain().CurrentSafeBlock() + require.Greater(t, l2SafeHead.Number.Uint64(), uint64(0)) + + // Run the FPP on one of the auto-derived blocks. + env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64()/2, testCfg.CheckResult, testCfg.InputParams...) +} + func Test_ProgramAction_SequenceWindowExpired(gt *testing.T) { matrix := helpers.NewMatrix[any]() defer matrix.Run(gt) @@ -67,4 +140,19 @@ func Test_ProgramAction_SequenceWindowExpired(gt *testing.T) { helpers.ExpectError(claim.ErrClaimNotValid), helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), ) + matrix.AddTestCase( + "ChannelCloseAfterWindowExpiry-HonestClaim", + nil, + helpers.LatestForkOnly, + runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test, + helpers.ExpectNoError(), + ) + matrix.AddTestCase( + "ChannelCloseAfterWindowExpiry-JunkClaim", + nil, + helpers.LatestForkOnly, + runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test, + helpers.ExpectError(claim.ErrClaimNotValid), + helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), + ) } From 2d8426f7ebe982be46442eca99d7e8a3d44cf6a2 Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 18 Sep 2024 20:40:34 -0400 Subject: [PATCH 2/3] buffer frame --- op-e2e/actions/proofs/sequence_window_expiry_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/op-e2e/actions/proofs/sequence_window_expiry_test.go b/op-e2e/actions/proofs/sequence_window_expiry_test.go index e27169746354..a575170d0712 100644 --- a/op-e2e/actions/proofs/sequence_window_expiry_test.go +++ b/op-e2e/actions/proofs/sequence_window_expiry_test.go @@ -80,6 +80,11 @@ func runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test(gt *testing.T, t l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock() require.EqualValues(t, 0, l2SafeHead.Number.Uint64()) + // Cache the next frame data before expiring the sequence window, but don't submit it yet. + env.Batcher.ActL2BatchBuffer(t) + env.Batcher.ActL2ChannelClose(t) + finalFrame := env.Batcher.ReadNextOutputFrame(t) + // Expire the sequence window by building `SequenceWindow + 1` empty blocks on L1. for i := 0; i < int(tp.SequencerWindowSize)+1; i++ { env.Alice.L1.ActResetTxOpts(t) @@ -93,10 +98,8 @@ func runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test(gt *testing.T, t env.Miner.ActL1FinalizeNext(t) } - // Instruct the batcher the channel on L1, after the sequence window + channel timeout has elapsed. - env.Batcher.ActL2BatchBuffer(t) - env.Batcher.ActL2ChannelClose(t) - env.Batcher.ActL2BatchSubmit(t) + // Instruct the batcher to closethe channel on L1, after the sequence window + channel timeout has elapsed. + env.Batcher.ActL2BatchSubmitRaw(t, finalFrame) env.Miner.ActL1StartBlock(12)(t) env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) env.Miner.ActL1EndBlock(t) From 0e9ce1d9d2e1657fd2917881c6beec93f03d7dc1 Mon Sep 17 00:00:00 2001 From: clabby Date: Wed, 18 Sep 2024 22:05:26 -0400 Subject: [PATCH 3/3] include extra tx --- op-e2e/actions/proofs/helpers/env.go | 18 +++++++++--------- op-e2e/actions/proofs/helpers/fixture.go | 4 ++-- op-e2e/actions/proofs/helpers/kona.go | 2 +- .../proofs/sequence_window_expiry_test.go | 4 ++++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index d911acff9306..df0f7e92bdc2 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -30,8 +30,8 @@ type L2FaultProofEnv struct { Sequencer *helpers.L2Sequencer Engine *helpers.L2Engine engCl *sources.EngineClient - sd *e2eutils.SetupData - dp *e2eutils.DeployParams + Sd *e2eutils.SetupData + Dp *e2eutils.DeployParams Miner *helpers.L1Miner Alice *helpers.CrossLayerUser } @@ -107,8 +107,8 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut Sequencer: sequencer, Engine: engine, engCl: engCl, - sd: sd, - dp: dp, + Sd: sd, + Dp: dp, Miner: miner, Alice: alice, } @@ -149,7 +149,7 @@ func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlock L2Claim: common.Hash(claimRoot.OutputRoot), L2Head: preRoot.BlockRef.Hash, L2OutputRoot: common.Hash(preRoot.OutputRoot), - L2ChainID: env.sd.RollupCfg.L2ChainID.Uint64(), + L2ChainID: env.Sd.RollupCfg.L2ChainID.Uint64(), L1Head: l1Head.Hash(), } for _, apply := range fixtureInputParams { @@ -162,7 +162,7 @@ func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlock fakeBeacon := fakebeacon.NewBeacon( env.log, env.Miner.BlobStore(), - env.sd.L1Cfg.Timestamp, + env.Sd.L1Cfg.Timestamp, 12, ) require.NoError(t, fakeBeacon.Start("127.0.0.1:0")) @@ -178,11 +178,11 @@ func (env *L2FaultProofEnv) RunFaultProofProgram(t helpers.Testing, l2ClaimBlock ) withInProcessPrefetcher := host.WithPrefetcher(func(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *config.Config) (host.Prefetcher, error) { // Set up in-process L1 sources - l1Cl := env.Miner.L1Client(t, env.sd.RollupCfg) + l1Cl := env.Miner.L1Client(t, env.Sd.RollupCfg) l1BlobFetcher := env.Miner.BlobSource() // Set up in-process L2 source - l2ClCfg := sources.L2ClientDefaultConfig(env.sd.RollupCfg, true) + l2ClCfg := sources.L2ClientDefaultConfig(env.Sd.RollupCfg, true) l2RPC := env.Engine.RPCClient() l2Client, err := host.NewL2Client(l2RPC, env.log, nil, &host.L2ClientConfig{L2ClientConfig: l2ClCfg, L2Head: cfg.L2Head}) require.NoError(t, err, "failed to create L2 client") @@ -238,7 +238,7 @@ func NewOpProgramCfg( fi *FixtureInputs, params ...OpProgramCfgParam, ) *config.Config { - dfault := config.NewConfig(env.sd.RollupCfg, env.sd.L2Cfg.Config, fi.L1Head, fi.L2Head, fi.L2OutputRoot, fi.L2Claim, fi.L2BlockNumber) + dfault := config.NewConfig(env.Sd.RollupCfg, env.Sd.L2Cfg.Config, fi.L1Head, fi.L2Head, fi.L2OutputRoot, fi.L2Claim, fi.L2BlockNumber) if dumpFixtures { dfault.DataDir = t.TempDir() diff --git a/op-e2e/actions/proofs/helpers/fixture.go b/op-e2e/actions/proofs/helpers/fixture.go index a0535a275e1a..892848470897 100644 --- a/op-e2e/actions/proofs/helpers/fixture.go +++ b/op-e2e/actions/proofs/helpers/fixture.go @@ -60,8 +60,8 @@ func tryDumpTestFixture( } name = convertToKebabCase(name) - rollupCfg := env.sd.RollupCfg - l2Genesis := env.sd.L2Cfg + rollupCfg := env.Sd.RollupCfg + l2Genesis := env.Sd.L2Cfg var expectedStatus uint8 if result == nil { diff --git a/op-e2e/actions/proofs/helpers/kona.go b/op-e2e/actions/proofs/helpers/kona.go index 289ff2c95895..9d34a98dda01 100644 --- a/op-e2e/actions/proofs/helpers/kona.go +++ b/op-e2e/actions/proofs/helpers/kona.go @@ -38,7 +38,7 @@ func RunKonaNative( ) error { // Write rollup config to tempdir. rollupConfigPath := filepath.Join(workDir, "rollup.json") - ser, err := json.Marshal(env.sd.RollupCfg) + ser, err := json.Marshal(env.Sd.RollupCfg) require.NoError(t, err) require.NoError(t, os.WriteFile(rollupConfigPath, ser, fs.ModePerm)) diff --git a/op-e2e/actions/proofs/sequence_window_expiry_test.go b/op-e2e/actions/proofs/sequence_window_expiry_test.go index a575170d0712..3f5ca9562d4b 100644 --- a/op-e2e/actions/proofs/sequence_window_expiry_test.go +++ b/op-e2e/actions/proofs/sequence_window_expiry_test.go @@ -58,6 +58,10 @@ func runSequenceWindowExpire_ChannelCloseAfterWindowExpiry_Test(gt *testing.T, t // Mine 2 empty blocks on L2. for i := 0; i < 2; i++ { env.Sequencer.ActL2StartBlock(t) + env.Alice.L2.ActResetTxOpts(t) + env.Alice.L2.ActSetTxToAddr(&env.Dp.Addresses.Bob) + env.Alice.L2.ActMakeTx(t) + env.Engine.ActL2IncludeTx(env.Alice.Address())(t) env.Sequencer.ActL2EndBlock(t) }