diff --git a/op-e2e/actions/proofs/isthmus_setcode_tx_test.go b/op-e2e/actions/proofs/isthmus_setcode_tx_test.go index bd63698a3ee43..b78a8754ee19e 100644 --- a/op-e2e/actions/proofs/isthmus_setcode_tx_test.go +++ b/op-e2e/actions/proofs/isthmus_setcode_tx_test.go @@ -2,6 +2,7 @@ package proofs_test import ( "bytes" + "math/rand" "testing" "github.com/ethereum/go-ethereum/common" @@ -12,14 +13,27 @@ 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-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/testutils" ) -var ( - aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") - bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") -) +func Test_ProgramAction_SetCodeTx(gt *testing.T) { + matrix := helpers.NewMatrix[any]() + defer matrix.Run(gt) + + matrix.AddDefaultTestCases( + nil, + helpers.LatestForkOnly, + runSetCodeTxTypeTest, + ) +} func runSetCodeTxTypeTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { + var ( + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") + ) + t := actionsHelpers.NewDefaultTesting(gt) // hardcoded because it's not available until after we need it @@ -127,13 +141,51 @@ func runSetCodeTxTypeTest(gt *testing.T, testCfg *helpers.TestCfg[any]) { env.RunFaultProofProgram(t, latestBlock.NumberU64(), testCfg.CheckResult, testCfg.InputParams...) } -func TestSetCodeTx(gt *testing.T) { +// TestInvalidSetCodeTxBatch tests that batches that include SetCodeTxs are dropped before Isthmus +func Test_ProgramAction_InvalidSetCodeTxBatch(gt *testing.T) { matrix := helpers.NewMatrix[any]() - defer matrix.Run(gt) - matrix.AddDefaultTestCases( nil, - helpers.LatestForkOnly, - runSetCodeTxTypeTest, + helpers.NewForkMatrix(helpers.Holocene), + testInvalidSetCodeTxBatch, ) + matrix.Run(gt) +} + +func testInvalidSetCodeTxBatch(gt *testing.T, testCfg *helpers.TestCfg[any]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg()) + sequencer := env.Sequencer + miner := env.Miner + batcher := env.Batcher + + sequencer.ActL2EmptyBlock(t) + u1 := sequencer.L2Unsafe() + sequencer.ActL2EmptyBlock(t) // we'll inject the setcode tx in this block's batch + + rng := rand.New(rand.NewSource(0)) + setcodetx := testutils.RandomSetCodeTx(rng, types.NewPragueSigner(env.Sd.RollupCfg.L2ChainID)) + batcher.ActL2BatchBuffer(t) + batcher.ActL2BatchBuffer(t, func(block *types.Block) *types.Block { + // inject user tx into upgrade batch + return block.WithBody(types.Body{Transactions: append(block.Transactions(), setcodetx)}) + }) + batcher.ActL2ChannelClose(t) + batcher.ActL2BatchSubmit(t) + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + miner.ActL1EndBlock(t) + + sequencer.ActL1HeadSignal(t) + sequencer.ActL2PipelineFull(t) + + l2safe := sequencer.L2Safe() + s2block := env.Engine.L2Chain().GetBlockByHash(l2safe.Hash) + require.Len(t, s2block.Transactions(), 1, "safe head should only contain l1 info deposit") + require.Equal(t, u1, l2safe, "expected last block to be reorgd out due to setcode tx") + + recs := env.Logs.FindLogs(testlog.NewMessageFilter("sequencers may not embed any SetCode transactions before Isthmus")) + require.Len(t, recs, 1) + + env.RunFaultProofProgram(t, l2safe.Number, testCfg.CheckResult, testCfg.InputParams...) } diff --git a/op-node/rollup/derive/batch_queue_test.go b/op-node/rollup/derive/batch_queue_test.go index 99e833d338093..54ae9abc63703 100644 --- a/op-node/rollup/derive/batch_queue_test.go +++ b/op-node/rollup/derive/batch_queue_test.go @@ -56,7 +56,7 @@ func mockHash(time uint64, layer uint8) common.Hash { func b(chainId *big.Int, timestamp uint64, epoch eth.L1BlockRef) *SingularBatch { rng := rand.New(rand.NewSource(int64(timestamp))) - signer := types.NewIsthmusSigner(chainId) + signer := types.NewLondonSigner(chainId) tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer) txData, _ := tx.MarshalBinary() return &SingularBatch{ diff --git a/op-node/rollup/derive/batches.go b/op-node/rollup/derive/batches.go index 4e3e20d1e50fc..102145f97a4e2 100644 --- a/op-node/rollup/derive/batches.go +++ b/op-node/rollup/derive/batches.go @@ -161,6 +161,8 @@ func checkSingularBatch(cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1Blo } } + isIsthmus := cfg.IsIsthmus(batch.Timestamp) + // We can do this check earlier, but it's a more intensive one, so we do this last. for i, txBytes := range batch.Transactions { if len(txBytes) == 0 { @@ -171,6 +173,10 @@ func checkSingularBatch(cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1Blo log.Warn("sequencers may not embed any deposits into batch data, but found tx that has one", "tx_index", i) return BatchDrop } + if !isIsthmus && txBytes[0] == types.SetCodeTxType { + log.Warn("sequencers may not embed any SetCode transactions before Isthmus", "tx_index", i) + return BatchDrop + } } return BatchAccept diff --git a/op-node/rollup/derive/batches_test.go b/op-node/rollup/derive/batches_test.go index dbb86aec22024..b434eb53009e3 100644 --- a/op-node/rollup/derive/batches_test.go +++ b/op-node/rollup/derive/batches_test.go @@ -573,7 +573,27 @@ func TestValidBatch(t *testing.T) { }, }, }, - Expected: BatchDrop, + Expected: BatchDrop, + ExpectedLog: "sequencers may not embed any deposits into batch data, but found tx that has one", + }, + { + Name: "setCode tx included pre-Isthmus", + L1Blocks: []eth.L1BlockRef{l1A, l1B}, + L2SafeHead: l2A0, + Batch: BatchWithL1InclusionBlock{ + L1InclusionBlock: l1B, + Batch: &SingularBatch{ + ParentHash: l2A1.ParentHash, + EpochNum: rollup.Epoch(l2A1.L1Origin.Number), + EpochHash: l2A1.L1Origin.Hash, + Timestamp: l2A1.Time, + Transactions: []hexutil.Bytes{ + []byte{types.SetCodeTxType, 0}, // piece of data alike to a SetCodeTx + }, + }, + }, + Expected: BatchDrop, + ExpectedLog: "sequencers may not embed any SetCode transactions before Isthmus", }, { Name: "valid batch same epoch", @@ -630,6 +650,7 @@ func TestValidBatch(t *testing.T) { Expected: BatchDrop, }, } + spanBatchTestCases := []ValidBatchTestCase{ { Name: "missing L1 info", diff --git a/op-service/testutils/random.go b/op-service/testutils/random.go index c7e37ef85ab16..277a29caeb397 100644 --- a/op-service/testutils/random.go +++ b/op-service/testutils/random.go @@ -141,8 +141,16 @@ func RandomTo(rng *rand.Rand) *common.Address { return &to } +func isIsthmusSigner(signer types.Signer) bool { + isthusSigner := types.NewIsthmusSigner(signer.ChainID()) + return signer.Equal(isthusSigner) +} + func RandomTx(rng *rand.Rand, baseFee *big.Int, signer types.Signer) *types.Transaction { - txTypeList := []int{types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.SetCodeTxType} + txTypeList := []int{types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType} + if isIsthmusSigner(signer) { + txTypeList = append(txTypeList, types.SetCodeTxType) + } txType := txTypeList[rng.Intn(len(txTypeList))] var tx *types.Transaction switch txType {