Skip to content

Commit

Permalink
feat(op-e2e): Channel timeout late submission test (#11995)
Browse files Browse the repository at this point in the history
* feat(op-e2e): Channel timeout late submission test

* remove finalization of l1 chain
  • Loading branch information
clabby authored Sep 19, 2024
1 parent bd97c32 commit e2a19f4
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 32 deletions.
161 changes: 132 additions & 29 deletions op-e2e/actions/proofs/channel_timeout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,17 @@ import (

actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-program/client/claim"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

// Run a test that exercises the channel timeout functionality in `op-program`.
//
// Steps:
// 1. Build `NumL2Blocks` empty blocks on L2.
// 2. Buffer the first half of the L2 blocks in the batcher, and submit the frame data.
// 3. Time out the channel by mining `ChannelTimeout + 1` empty blocks on L1.
// 4. Submit the channel frame data across 2 transactions.
// 5. Instruct the sequencer to derive the L2 chain.
// 6. Run the FPP on the safe head.
// Run a test that submits the first channel frame, times out the channel, and then resubmits the full channel.
func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt)
tp := helpers.NewTestParams(func(tp *e2eutils.TestParams) {
// Set the channel timeout to 10 blocks, 12x lower than the sequencing window.
tp.ChannelTimeout = 10
})
tp := helpers.NewTestParams()
env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg())
channelTimeout := env.Sd.ChainSpec.ChannelTimeout(0)

const NumL2Blocks = 10

Expand All @@ -40,16 +29,16 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
for i := 0; i < NumL2Blocks/2; i++ {
env.Batcher.ActL2BatchBuffer(t)
}
env.Batcher.ActL2BatchSubmit(t)
firstFrame := env.Batcher.ReadNextOutputFrame(t)
env.Batcher.ActL2BatchSubmitRaw(t, firstFrame)

// Instruct the batcher to submit the first channel frame to L1, and include the transaction.
// Include the batcher transaction.
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)
Expand All @@ -59,11 +48,10 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock()
require.Equal(t, uint64(0), l2SafeHead.Number.Uint64())

// Time out the channel by mining `ChannelTimeout + 1` empty blocks on L1.
for i := uint64(0); i < tp.ChannelTimeout+1; i++ {
// Time out the channel by mining `channelTimeout + 1` empty blocks on L1.
for i := uint64(0); i < channelTimeout+1; i++ {
env.Miner.ActEmptyBlock(t)
env.Miner.ActL1SafeNext(t)
env.Miner.ActL1FinalizeNext(t)
}

// Instruct the sequencer to derive the L2 chain - the channel should now be timed out.
Expand All @@ -77,24 +65,124 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
// Instruct the batcher to submit the blocks to L1 in a new channel,
// submitted across 2 transactions.
for i := 0; i < 2; i++ {
// Buffer half of the L2 chain's blocks.
for j := 0; j < NumL2Blocks/2; j++ {
env.Batcher.ActL2BatchBuffer(t)
}

// Close the channel on the second iteration.
if i == 1 {
if i == 0 {
// Re-submit the first frame
env.Batcher.ActL2BatchSubmitRaw(t, firstFrame)
} else {
// Buffer half of the L2 chain's blocks.
for j := 0; j < NumL2Blocks/2; j++ {
env.Batcher.ActL2BatchBuffer(t)
}
env.Batcher.ActL2ChannelClose(t)
env.Batcher.ActL2BatchSubmit(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 frame data on L1.
env.Miner.ActL1SafeNext(t)
env.Miner.ActL1FinalizeNext(t)
}

// Instruct the sequencer to derive the L2 chain.
env.Sequencer.ActL1HeadSignal(t)
env.Sequencer.ActL2PipelineFull(t)

// Ensure the safe head has still advanced to L2 block # NumL2Blocks.
l2SafeHead = env.Engine.L2Chain().CurrentSafeBlock()
require.EqualValues(t, NumL2Blocks, l2SafeHead.Number.Uint64())

// Run the FPP on L2 block # NumL2Blocks/2.
env.RunFaultProofProgram(t, NumL2Blocks/2, testCfg.CheckResult, testCfg.InputParams...)
}

func runChannelTimeoutTest_CloseChannelLate(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt)
tp := helpers.NewTestParams()
env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg())
channelTimeout := env.Sd.ChainSpec.ChannelTimeout(0)

const NumL2Blocks = 10

// Build NumL2Blocks empty blocks on L2
for i := 0; i < NumL2Blocks; i++ {
env.Sequencer.ActL2StartBlock(t)
env.Sequencer.ActL2EndBlock(t)
}

// Buffer the first half of L2 blocks in the batcher, and submit it.
for i := 0; i < NumL2Blocks/2; i++ {
env.Batcher.ActL2BatchBuffer(t)
}
firstFrame := env.Batcher.ReadNextOutputFrame(t)
env.Batcher.ActL2BatchSubmitRaw(t, firstFrame)

// Instruct the batcher to submit the first channel frame to L1, and include the transaction.
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)

// 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 that the safe head has not advanced - the channel is incomplete.
l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock()
require.Equal(t, uint64(0), l2SafeHead.Number.Uint64())

// Time out the channel by mining `channelTimeout + 1` empty blocks on L1.
for i := uint64(0); i < channelTimeout+1; i++ {
env.Miner.ActEmptyBlock(t)
env.Miner.ActL1SafeNext(t)
}

// Instruct the sequencer to derive the L2 chain.
env.Sequencer.ActL1HeadSignal(t)
env.Sequencer.ActL2PipelineFull(t)

// Ensure the safe head has still not advanced.
l2SafeHead = env.Engine.L2Chain().CurrentSafeBlock()
require.Equal(t, uint64(0), l2SafeHead.Number.Uint64())

// Cache the second and final frame of the channel from the batcher, but do not submit it yet.
for i := 0; i < NumL2Blocks/2; i++ {
env.Batcher.ActL2BatchBuffer(t)
}
env.Batcher.ActL2ChannelClose(t)
finalFrame := env.Batcher.ReadNextOutputFrame(t)

// Submit the final frame of the timed out channel, now that the channel has timed out.
env.Batcher.ActL2BatchSubmitRaw(t, finalFrame)

// Instruct the batcher to submit the second channel frame to L1, and include the transaction.
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)

// 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 has still not advanced.
l2SafeHead = env.Engine.L2Chain().CurrentSafeBlock()
require.Equal(t, uint64(0), l2SafeHead.Number.Uint64())

// Instruct the batcher to submit the blocks to L1 in a new channel.
for _, frame := range [][]byte{firstFrame, finalFrame} {
env.Batcher.ActL2BatchSubmitRaw(t, frame)
env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t)

// Finalize the block with the resubmitted channel frames on L1.
env.Miner.ActL1SafeNext(t)
}

// Instruct the sequencer to derive the L2 chain.
Expand Down Expand Up @@ -128,4 +216,19 @@ func Test_ProgramAction_ChannelTimeout(gt *testing.T) {
helpers.ExpectError(claim.ErrClaimNotValid),
helpers.WithL2Claim(common.HexToHash("0xdeadbeef")),
)
matrix.AddTestCase(
"CloseChannelLate-HonestClaim",
nil,
helpers.LatestForkOnly,
runChannelTimeoutTest_CloseChannelLate,
helpers.ExpectNoError(),
)
matrix.AddTestCase(
"CloseChannelLate-JunkClaim",
nil,
helpers.LatestForkOnly,
runChannelTimeoutTest_CloseChannelLate,
helpers.ExpectError(claim.ErrClaimNotValid),
helpers.WithL2Claim(common.HexToHash("0xdeadbeef")),
)
}
6 changes: 3 additions & 3 deletions op-e2e/actions/proofs/helpers/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type L2FaultProofEnv struct {

func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg) *L2FaultProofEnv {
log := testlog.Logger(t, log.LvlDebug)
dp := NewDeployParams(t, func(dp *e2eutils.DeployParams) {
dp := NewDeployParams(t, tp, func(dp *e2eutils.DeployParams) {
genesisBlock := hexutil.Uint64(0)

// Enable cancun always
Expand Down Expand Up @@ -208,8 +208,8 @@ func NewTestParams(params ...TestParam) *e2eutils.TestParams {

type DeployParam func(p *e2eutils.DeployParams)

func NewDeployParams(t helpers.Testing, params ...DeployParam) *e2eutils.DeployParams {
dfault := e2eutils.MakeDeployParams(t, NewTestParams())
func NewDeployParams(t helpers.Testing, tp *e2eutils.TestParams, params ...DeployParam) *e2eutils.DeployParams {
dfault := e2eutils.MakeDeployParams(t, tp)
for _, apply := range params {
apply(dfault)
}
Expand Down

0 comments on commit e2a19f4

Please sign in to comment.