Commit e2a19f45 authored by clabby's avatar clabby Committed by GitHub

feat(op-e2e): Channel timeout late submission test (#11995)

* feat(op-e2e): Channel timeout late submission test

* remove finalization of l1 chain
parent bd97c322
...@@ -5,28 +5,17 @@ import ( ...@@ -5,28 +5,17 @@ import (
actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" 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/actions/proofs/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-program/client/claim" "github.com/ethereum-optimism/optimism/op-program/client/claim"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// Run a test that exercises the channel timeout functionality in `op-program`. // Run a test that submits the first channel frame, times out the channel, and then resubmits the full channel.
//
// 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.
func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt) t := actionsHelpers.NewDefaultTesting(gt)
tp := helpers.NewTestParams(func(tp *e2eutils.TestParams) { tp := helpers.NewTestParams()
// Set the channel timeout to 10 blocks, 12x lower than the sequencing window.
tp.ChannelTimeout = 10
})
env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg()) env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg())
channelTimeout := env.Sd.ChainSpec.ChannelTimeout(0)
const NumL2Blocks = 10 const NumL2Blocks = 10
...@@ -40,16 +29,16 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { ...@@ -40,16 +29,16 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
for i := 0; i < NumL2Blocks/2; i++ { for i := 0; i < NumL2Blocks/2; i++ {
env.Batcher.ActL2BatchBuffer(t) 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.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t) env.Miner.ActL1EndBlock(t)
// Finalize the block with the first channel frame on L1. // Finalize the block with the first channel frame on L1.
env.Miner.ActL1SafeNext(t) 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. // Instruct the sequencer to derive the L2 chain from the data on L1 that the batcher just posted.
env.Sequencer.ActL1HeadSignal(t) env.Sequencer.ActL1HeadSignal(t)
...@@ -59,11 +48,10 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { ...@@ -59,11 +48,10 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock() l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock()
require.Equal(t, uint64(0), l2SafeHead.Number.Uint64()) require.Equal(t, uint64(0), l2SafeHead.Number.Uint64())
// Time out the channel by mining `ChannelTimeout + 1` empty blocks on L1. // Time out the channel by mining `channelTimeout + 1` empty blocks on L1.
for i := uint64(0); i < tp.ChannelTimeout+1; i++ { for i := uint64(0); i < channelTimeout+1; i++ {
env.Miner.ActEmptyBlock(t) env.Miner.ActEmptyBlock(t)
env.Miner.ActL1SafeNext(t) env.Miner.ActL1SafeNext(t)
env.Miner.ActL1FinalizeNext(t)
} }
// Instruct the sequencer to derive the L2 chain - the channel should now be timed out. // Instruct the sequencer to derive the L2 chain - the channel should now be timed out.
...@@ -77,24 +65,124 @@ func runChannelTimeoutTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { ...@@ -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, // Instruct the batcher to submit the blocks to L1 in a new channel,
// submitted across 2 transactions. // submitted across 2 transactions.
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
// Buffer half of the L2 chain's blocks. if i == 0 {
for j := 0; j < NumL2Blocks/2; j++ { // Re-submit the first frame
env.Batcher.ActL2BatchBuffer(t) env.Batcher.ActL2BatchSubmitRaw(t, firstFrame)
} } else {
// Buffer half of the L2 chain's blocks.
// Close the channel on the second iteration. for j := 0; j < NumL2Blocks/2; j++ {
if i == 1 { env.Batcher.ActL2BatchBuffer(t)
}
env.Batcher.ActL2ChannelClose(t) env.Batcher.ActL2ChannelClose(t)
env.Batcher.ActL2BatchSubmit(t)
} }
env.Batcher.ActL2BatchSubmit(t)
env.Miner.ActL1StartBlock(12)(t) env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t) env.Miner.ActL1EndBlock(t)
// Finalize the block with the frame data on L1. // Finalize the block with the frame data on L1.
env.Miner.ActL1SafeNext(t) 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. // Instruct the sequencer to derive the L2 chain.
...@@ -128,4 +216,19 @@ func Test_ProgramAction_ChannelTimeout(gt *testing.T) { ...@@ -128,4 +216,19 @@ func Test_ProgramAction_ChannelTimeout(gt *testing.T) {
helpers.ExpectError(claim.ErrClaimNotValid), helpers.ExpectError(claim.ErrClaimNotValid),
helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), 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")),
)
} }
...@@ -38,7 +38,7 @@ type L2FaultProofEnv struct { ...@@ -38,7 +38,7 @@ type L2FaultProofEnv struct {
func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg) *L2FaultProofEnv { func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eutils.TestParams, batcherCfg *helpers.BatcherCfg) *L2FaultProofEnv {
log := testlog.Logger(t, log.LvlDebug) 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) genesisBlock := hexutil.Uint64(0)
// Enable cancun always // Enable cancun always
...@@ -208,8 +208,8 @@ func NewTestParams(params ...TestParam) *e2eutils.TestParams { ...@@ -208,8 +208,8 @@ func NewTestParams(params ...TestParam) *e2eutils.TestParams {
type DeployParam func(p *e2eutils.DeployParams) type DeployParam func(p *e2eutils.DeployParams)
func NewDeployParams(t helpers.Testing, params ...DeployParam) *e2eutils.DeployParams { func NewDeployParams(t helpers.Testing, tp *e2eutils.TestParams, params ...DeployParam) *e2eutils.DeployParams {
dfault := e2eutils.MakeDeployParams(t, NewTestParams()) dfault := e2eutils.MakeDeployParams(t, tp)
for _, apply := range params { for _, apply := range params {
apply(dfault) apply(dfault)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment