Commit 6c989461 authored by clabby's avatar clabby Committed by GitHub

feat(op-e2e): L1 lookback action tests (#12043)

* feat(op-e2e): L1 lookback action tests

* review

* adrian review
parent de7f8d9b
...@@ -150,13 +150,9 @@ func (s *L2Batcher) ActL2BatchBuffer(t Testing) { ...@@ -150,13 +150,9 @@ func (s *L2Batcher) ActL2BatchBuffer(t Testing) {
require.NoError(t, s.Buffer(t), "failed to add block to channel") require.NoError(t, s.Buffer(t), "failed to add block to channel")
} }
func (s *L2Batcher) Buffer(t Testing) error {
return s.BufferWithOpts(t)
}
type BlockModifier = func(block *types.Block) type BlockModifier = func(block *types.Block)
func (s *L2Batcher) BufferWithOpts(t Testing, opts ...BlockModifier) error { func (s *L2Batcher) Buffer(t Testing, opts ...BlockModifier) error {
if s.l2Submitting { // break ongoing submitting work if necessary if s.l2Submitting { // break ongoing submitting work if necessary
s.L2ChannelOut = nil s.L2ChannelOut = nil
s.l2Submitting = false s.l2Submitting = false
......
...@@ -235,9 +235,7 @@ func (s *BasicUser[B]) LastTxReceipt(t Testing) *types.Receipt { ...@@ -235,9 +235,7 @@ func (s *BasicUser[B]) LastTxReceipt(t Testing) *types.Receipt {
return receipt return receipt
} }
// ActMakeTx makes a tx with the predetermined contents (see randomization and other actions) func (s *BasicUser[B]) MakeTransaction(t Testing) *types.Transaction {
// and sends it to the tx pool
func (s *BasicUser[B]) ActMakeTx(t Testing) {
gas, err := s.env.EthCl.EstimateGas(t.Ctx(), ethereum.CallMsg{ gas, err := s.env.EthCl.EstimateGas(t.Ctx(), ethereum.CallMsg{
From: s.address, From: s.address,
To: s.txToAddr, To: s.txToAddr,
...@@ -247,7 +245,7 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) { ...@@ -247,7 +245,7 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) {
Data: s.txCallData, Data: s.txCallData,
}) })
require.NoError(t, err, "gas estimation should pass") require.NoError(t, err, "gas estimation should pass")
tx := types.MustSignNewTx(s.account, s.env.Signer, &types.DynamicFeeTx{ return types.MustSignNewTx(s.account, s.env.Signer, &types.DynamicFeeTx{
To: s.txToAddr, To: s.txToAddr,
GasFeeCap: s.txOpts.GasFeeCap, GasFeeCap: s.txOpts.GasFeeCap,
GasTipCap: s.txOpts.GasTipCap, GasTipCap: s.txOpts.GasTipCap,
...@@ -257,7 +255,13 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) { ...@@ -257,7 +255,13 @@ func (s *BasicUser[B]) ActMakeTx(t Testing) {
Gas: gas, Gas: gas,
Data: s.txCallData, Data: s.txCallData,
}) })
err = s.env.EthCl.SendTransaction(t.Ctx(), tx) }
// ActMakeTx makes a tx with the predetermined contents (see randomization and other actions)
// and sends it to the tx pool
func (s *BasicUser[B]) ActMakeTx(t Testing) {
tx := s.MakeTransaction(t)
err := s.env.EthCl.SendTransaction(t.Ctx(), tx)
require.NoError(t, err, "must send tx") require.NoError(t, err, "must send tx")
s.lastTxHash = tx.Hash() s.lastTxHash = tx.Hash()
// reset the calldata // reset the calldata
......
...@@ -26,7 +26,7 @@ func runBadTxInBatchTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { ...@@ -26,7 +26,7 @@ func runBadTxInBatchTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
env.Alice.L2.ActCheckReceiptStatusOfLastTx(true)(t) env.Alice.L2.ActCheckReceiptStatusOfLastTx(true)(t)
// Instruct the batcher to submit a faulty channel, with an invalid tx. // Instruct the batcher to submit a faulty channel, with an invalid tx.
err := env.Batcher.BufferWithOpts(t, func(block *types.Block) { err := env.Batcher.Buffer(t, func(block *types.Block) {
// Replace the tx with one that has a bad signature. // Replace the tx with one that has a bad signature.
txs := block.Transactions() txs := block.Transactions()
newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65))
...@@ -91,7 +91,7 @@ func runBadTxInBatch_ResubmitBadFirstFrame_Test(gt *testing.T, testCfg *helpers. ...@@ -91,7 +91,7 @@ func runBadTxInBatch_ResubmitBadFirstFrame_Test(gt *testing.T, testCfg *helpers.
// Instruct the batcher to submit a faulty channel, with an invalid tx in the second block // Instruct the batcher to submit a faulty channel, with an invalid tx in the second block
// within the span batch. // within the span batch.
env.Batcher.ActL2BatchBuffer(t) env.Batcher.ActL2BatchBuffer(t)
err := env.Batcher.BufferWithOpts(t, func(block *types.Block) { err := env.Batcher.Buffer(t, func(block *types.Block) {
// Replace the tx with one that has a bad signature. // Replace the tx with one that has a bad signature.
txs := block.Transactions() txs := block.Transactions()
newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65)) newTx, err := txs[1].WithSignature(env.Alice.L2.Signer(), make([]byte, 65))
......
...@@ -34,6 +34,7 @@ type L2FaultProofEnv struct { ...@@ -34,6 +34,7 @@ type L2FaultProofEnv struct {
Dp *e2eutils.DeployParams Dp *e2eutils.DeployParams
Miner *helpers.L1Miner Miner *helpers.L1Miner
Alice *helpers.CrossLayerUser Alice *helpers.CrossLayerUser
Bob *helpers.CrossLayerUser
} }
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 {
...@@ -100,6 +101,9 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut ...@@ -100,6 +101,9 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut
alice := helpers.NewCrossLayerUser(log, dp.Secrets.Alice, rand.New(rand.NewSource(0xa57b))) alice := helpers.NewCrossLayerUser(log, dp.Secrets.Alice, rand.New(rand.NewSource(0xa57b)))
alice.L1.SetUserEnv(l1UserEnv) alice.L1.SetUserEnv(l1UserEnv)
alice.L2.SetUserEnv(l2UserEnv) alice.L2.SetUserEnv(l2UserEnv)
bob := helpers.NewCrossLayerUser(log, dp.Secrets.Bob, rand.New(rand.NewSource(0xbeef)))
bob.L1.SetUserEnv(l1UserEnv)
bob.L2.SetUserEnv(l2UserEnv)
return &L2FaultProofEnv{ return &L2FaultProofEnv{
log: log, log: log,
...@@ -111,6 +115,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut ...@@ -111,6 +115,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut
Dp: dp, Dp: dp,
Miner: miner, Miner: miner,
Alice: alice, Alice: alice,
Bob: bob,
} }
} }
......
package proofs
import (
"testing"
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-program/client/claim"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
func runL1LookbackTest(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt)
tp := helpers.NewTestParams()
env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg())
const numL2Blocks = 8
for i := 0; i < numL2Blocks; i++ {
// Create an empty L2 block.
env.Sequencer.ActL2StartBlock(t)
env.Sequencer.ActL2EndBlock(t)
// Buffer the L2 block in the batcher.
env.Batcher.ActBufferAll(t)
if i == numL2Blocks-1 {
env.Batcher.ActL2ChannelClose(t)
}
env.Batcher.ActL2BatchSubmit(t)
// Include the frame on L1.
env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t)
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 advanced to `NumL2Blocks`.
l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock()
require.EqualValues(t, numL2Blocks, l2SafeHead.Number.Uint64())
// Run the FPP on the configured L2 block.
env.RunFaultProofProgram(t, numL2Blocks/2, testCfg.CheckResult, testCfg.InputParams...)
}
func runL1LookbackTest_ReopenChannel(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt)
tp := helpers.NewTestParams()
env := helpers.NewL2FaultProofEnv(t, testCfg, tp, helpers.NewBatcherCfg())
// Create an L2 block with 1 transaction.
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)
l2BlockBeforeDerive := env.Engine.L2Chain().CurrentBlock()
// Buffer the L2 block in the batcher.
env.Batcher.ActL2BatchBuffer(t)
env.Batcher.ActL2BatchSubmit(t)
// Include the frame on L1.
env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t)
env.Miner.ActL1SafeNext(t)
// Re-submit the first L2 block frame w/ different transaction data.
err := env.Batcher.Buffer(t, func(block *types.Block) {
env.Bob.L2.ActResetTxOpts(t)
env.Bob.L2.ActSetTxToAddr(&env.Dp.Addresses.Mallory)
tx := env.Bob.L2.MakeTransaction(t)
block.Transactions()[1] = tx
})
require.NoError(t, err)
env.Batcher.ActL2BatchSubmit(t)
// Include the duplicate frame on L1.
env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t)
env.Miner.ActL1SafeNext(t)
const numL2Blocks = 8
for i := 1; i < numL2Blocks; i++ {
// Create an empty L2 block.
env.Sequencer.ActL2StartBlock(t)
env.Sequencer.ActL2EndBlock(t)
// Buffer the L2 block in the batcher.
env.Batcher.ActBufferAll(t)
if i == numL2Blocks-1 {
env.Batcher.ActL2ChannelClose(t)
}
env.Batcher.ActL2BatchSubmit(t)
// Include the frame on L1.
env.Miner.ActL1StartBlock(12)(t)
env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t)
env.Miner.ActL1EndBlock(t)
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 correct block was derived.
l2BlockAfterDerive := env.Engine.L2Chain().GetBlockByNumber(1)
require.EqualValues(t, l2BlockAfterDerive.Hash(), l2BlockBeforeDerive.Hash())
// Ensure that the safe head has advanced to `NumL2Blocks`.
l2SafeHead := env.Engine.L2Chain().CurrentSafeBlock()
require.EqualValues(t, numL2Blocks, l2SafeHead.Number.Uint64())
// Run the FPP on the configured L2 block.
env.RunFaultProofProgram(t, numL2Blocks/2, testCfg.CheckResult, testCfg.InputParams...)
}
func Test_ProgramAction_L1Lookback(gt *testing.T) {
matrix := helpers.NewMatrix[any]()
defer matrix.Run(gt)
matrix.AddTestCase(
"HonestClaim",
nil,
helpers.LatestForkOnly,
runL1LookbackTest,
helpers.ExpectNoError(),
)
matrix.AddTestCase(
"JunkClaim",
nil,
helpers.LatestForkOnly,
runL1LookbackTest,
helpers.ExpectError(claim.ErrClaimNotValid),
helpers.WithL2Claim(common.HexToHash("0xdeadbeef")),
)
matrix.AddTestCase(
"HonestClaim-ReopenChannel",
nil,
helpers.LatestForkOnly,
runL1LookbackTest_ReopenChannel,
helpers.ExpectNoError(),
)
matrix.AddTestCase(
"JunkClaim-ReopenChannel",
nil,
helpers.LatestForkOnly,
runL1LookbackTest_ReopenChannel,
helpers.ExpectError(claim.ErrClaimNotValid),
helpers.WithL2Claim(common.HexToHash("0xdeadbeef")),
)
}
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