diff --git a/op-e2e/actions/helpers/l2_sequencer.go b/op-e2e/actions/helpers/l2_sequencer.go index 37d93210f7e70..b03724b6cb978 100644 --- a/op-e2e/actions/helpers/l2_sequencer.go +++ b/op-e2e/actions/helpers/l2_sequencer.go @@ -204,6 +204,12 @@ func (s *L2Sequencer) ActBuildL2ToTime(t Testing, target uint64) { } } +func (s *L2Sequencer) ActBuildL2ToOffset(t Testing, offset uint64) { + for s.L2Unsafe().Time < s.RollupCfg.Genesis.L2Time+offset { + s.ActL2EmptyBlock(t) + } +} + func (s *L2Sequencer) ActBuildL2ToCanyon(t Testing) { require.NotNil(t, s.RollupCfg.CanyonTime, "cannot activate CanyonTime when it is not scheduled") for s.L2Unsafe().Time < *s.RollupCfg.CanyonTime { @@ -245,3 +251,10 @@ func (s *L2Sequencer) ActBuildL2ToIsthmus(t Testing) { s.ActL2EmptyBlock(t) } } + +func (s *L2Sequencer) ActBuildL2ToFork(t Testing, fork rollup.ForkName) { + // TODO: add check that fork is set in rollup config + for s.RollupCfg.IsActivationBlock(s.L2Unsafe().Time-s.RollupCfg.BlockTime, s.L2Unsafe().Time) != fork { + s.ActL2EmptyBlock(t) + } +} diff --git a/op-e2e/actions/helpers/user.go b/op-e2e/actions/helpers/user.go index d7215b80650f7..5cdb453ff41a1 100644 --- a/op-e2e/actions/helpers/user.go +++ b/op-e2e/actions/helpers/user.go @@ -259,13 +259,14 @@ func (s *BasicUser[B]) MakeTransaction(t Testing) *types.Transaction { // 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) { +func (s *BasicUser[B]) ActMakeTx(t Testing) *types.Transaction { tx := s.MakeTransaction(t) err := s.env.EthCl.SendTransaction(t.Ctx(), tx) require.NoError(t, err, "must send tx") s.lastTxHash = tx.Hash() // reset the calldata s.txCallData = []byte{} + return tx } func (s *BasicUser[B]) ActCheckReceiptStatusOfLastTx(success bool) func(t Testing) { diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index d9490f2fd42fe..7d68c230c506b 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -66,7 +66,11 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut case Holocene: dp.DeployConfig.ActivateForkAtGenesis(rollup.Holocene) case Isthmus: + // Isthmus usually runs on a Prague L1 network + dp.DeployConfig.L1PragueTimeOffset = &genesisBlock dp.DeployConfig.ActivateForkAtGenesis(rollup.Isthmus) + case Interop: + dp.DeployConfig.ActivateForkAtGenesis(rollup.Interop) } for _, override := range deployConfigOverrides { diff --git a/op-e2e/actions/proofs/helpers/matrix.go b/op-e2e/actions/proofs/helpers/matrix.go index c679f03584163..6cc85e8157bce 100644 --- a/op-e2e/actions/proofs/helpers/matrix.go +++ b/op-e2e/actions/proofs/helpers/matrix.go @@ -115,9 +115,11 @@ var ( Granite = &Hardfork{Name: "Granite", Precedence: 6} Holocene = &Hardfork{Name: "Holocene", Precedence: 7} Isthmus = &Hardfork{Name: "Isthmus", Precedence: 8} + Interop = &Hardfork{Name: "Interop", Precedence: 9} ) var ( + // Not adding Interop yet Hardforks = ForkMatrix{Regolith, Canyon, Delta, Ecotone, Fjord, Granite, Holocene, Isthmus} LatestFork = Hardforks[len(Hardforks)-1] LatestForkOnly = ForkMatrix{LatestFork} diff --git a/op-e2e/actions/proofs/upgrade_block_test.go b/op-e2e/actions/proofs/upgrade_block_test.go new file mode 100644 index 0000000000000..52d0979a33833 --- /dev/null +++ b/op-e2e/actions/proofs/upgrade_block_test.go @@ -0,0 +1,159 @@ +package proofs_test + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + 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-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +type upgradeBlockTestCfg struct { + fork rollup.ForkName + numUpgradeTxs int +} + +// TestUpgradeBlockGas tests that the upgrade block correctly adds ugprade gas +func TestUpgradeBlockGas(gt *testing.T) { + matrix := helpers.NewMatrix[upgradeBlockTestCfg]() + defer matrix.Run(gt) + + matrix.AddDefaultTestCases( + upgradeBlockTestCfg{fork: rollup.Isthmus, numUpgradeTxs: 8}, + helpers.NewForkMatrix(helpers.Holocene), + testUpgradeBlockGas, + ) //.AddDefaultTestCases( + // upgradeBlockTestCfg{fork: rollup.Interop, numUpgradeTxs: 0}, + // helpers.NewForkMatrix(helpers.Isthmus), + // testUpgradeBlock, + // ) +} + +func testUpgradeBlockGas(gt *testing.T, testCfg *helpers.TestCfg[upgradeBlockTestCfg]) { + tcfg := testCfg.Custom + t := actionsHelpers.NewDefaultTesting(gt) + testSetup := func(dc *genesis.DeployConfig) { + dc.L1PragueTimeOffset = ptr(hexutil.Uint64(0)) + // activate fork after a few blocks + dc.SetForkTimeOffset(tcfg.fork, ptr(uint64(4))) + } + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), testSetup) + + engine := env.Engine + sequencer := env.Sequencer + miner := env.Miner + rollupCfg := env.Sd.RollupCfg + + miner.ActEmptyBlock(t) + + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildL2ToFork(t, tcfg.fork) + + upgradeHeader := engine.L2Chain().CurrentHeader() + require.Equal(t, tcfg.fork, rollupCfg.IsActivationBlock(upgradeHeader.Time-1, upgradeHeader.Time)) + + upgradeBlock := engine.L2Chain().GetBlockByHash(upgradeHeader.Hash()) + require.Len(t, upgradeBlock.Transactions(), tcfg.numUpgradeTxs+1) + var upgradeGas uint64 + for _, tx := range upgradeBlock.Transactions()[1:] /* skip l1 info deposit */ { + upgradeGas += tx.Gas() + } + require.Equal(t, rollupCfg.Genesis.SystemConfig.GasLimit+upgradeGas, upgradeBlock.GasLimit()) + + env.BatchAndMine(t) + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + l2SafeHead := engine.L2Chain().CurrentSafeBlock() + require.Equal(t, eth.HeaderBlockID(l2SafeHead), eth.HeaderBlockID(upgradeHeader), "derivation leads to the same block") + + env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64(), testCfg.CheckResult, testCfg.InputParams...) +} + +// TestUpgradeBlockTxOmission tests that the sequencer omits user transactions in upgrade blocks +// and that batches that contain user transactions in an upgrade block are dropped. +func TestUpgradeBlockTxOmission(gt *testing.T) { + matrix := helpers.NewMatrix[upgradeBlockTestCfg]() + defer matrix.Run(gt) + + matrix.AddDefaultTestCases( + upgradeBlockTestCfg{fork: rollup.Isthmus, numUpgradeTxs: 8}, + helpers.NewForkMatrix(helpers.Holocene), + testUpgradeBlockTxOmission, + ) //.AddDefaultTestCases( + // upgradeBlockTestCfg{fork: rollup.Interop, numUpgradeTxs: 0}, + // helpers.NewForkMatrix(helpers.Isthmus), + // testUpgradeBlockTxOmission, + // ) +} + +func testUpgradeBlockTxOmission(gt *testing.T, testCfg *helpers.TestCfg[upgradeBlockTestCfg]) { + tcfg := testCfg.Custom + t := actionsHelpers.NewDefaultTesting(gt) + offset := uint64(4) + testSetup := func(dc *genesis.DeployConfig) { + dc.L1PragueTimeOffset = ptr(hexutil.Uint64(0)) + // activate fork after a few blocks + dc.SetForkTimeOffset(tcfg.fork, &offset) + } + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), testSetup) + + engine := env.Engine + sequencer := env.Sequencer + miner := env.Miner + rollupCfg := env.Sd.RollupCfg + blockTime := rollupCfg.BlockTime + + miner.ActEmptyBlock(t) + + sequencer.ActL1HeadSignal(t) + for i := 0; i < int(offset)-1; i++ { + sequencer.ActL2EmptyBlock(t) + } + tx := env.Alice.L2.ActMakeTx(t) + sequencer.ActL2StartBlock(t) + // we assert later that the sequencer actually omits this tx in the upgrade block + engine.ActL2IncludeTx(env.Alice.Address()) + sequencer.ActL2EndBlock(t) + + upgradeHeader := engine.L2Chain().CurrentHeader() + require.Equal(t, tcfg.fork, + rollupCfg.IsActivationBlock(upgradeHeader.Time-blockTime, upgradeHeader.Time), + "this block should be upgrade block") + upgradeBlock := engine.L2Chain().GetBlockByHash(upgradeHeader.Hash()) + require.Len(t, upgradeBlock.Transactions(), tcfg.numUpgradeTxs+1, "upgrade block doesn't contain alice tx") + + batcher := env.Batcher + for i := 0; i < int(offset)-1; i++ { + 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(), tx)}) + }) + + batcher.ActL2ChannelClose(t) + batcher.ActL2BatchSubmit(t) + env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) + env.Miner.ActL1EndBlock(t) + + env.Sequencer.ActL1HeadSignal(t) + env.Sequencer.ActL2PipelineFull(t) + + recs := env.Logs.FindLogs(testlog.NewMessageFilter("dropping batch with user transactions in fork activation block")) + require.Len(t, recs, 1) + + l2SafeHead := engine.L2Chain().CurrentSafeBlock() + preUpgradeHeader := engine.L2Chain().GetHeaderByHash(upgradeHeader.ParentHash) + require.Equal(t, eth.HeaderBlockID(preUpgradeHeader), eth.HeaderBlockID(l2SafeHead), "derivation only reaches pre-upgrade block") + + env.RunFaultProofProgram(t, l2SafeHead.Number.Uint64(), testCfg.CheckResult, testCfg.InputParams...) +} diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index ea060f996456f..883866ca39daa 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -124,12 +124,16 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex upgradeTxs = append(upgradeTxs, fjord...) } + // Starting with Isthmus, we add upgrade gas to the upgrade block so we don't need to + // rely on the upgrade transactions to fit within the system tx gas limit. + var upgradeGas uint64 if ba.rollupCfg.IsIsthmusActivationBlock(nextL2Time) { - isthmus, err := IsthmusNetworkUpgradeTransactions() + isthmus, isthmusGas, err := IsthmusNetworkUpgradeTransactions() if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to build isthmus network upgrade txs: %w", err)) } upgradeTxs = append(upgradeTxs, isthmus...) + upgradeGas += isthmusGas } l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, sysConfig, seqNumber, l1Info, nextL2Time) @@ -165,13 +169,15 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex } } + gasLimit := sysConfig.GasLimit + upgradeGas + r := ð.PayloadAttributes{ Timestamp: hexutil.Uint64(nextL2Time), PrevRandao: eth.Bytes32(l1Info.MixDigest()), SuggestedFeeRecipient: predeploys.SequencerFeeVaultAddr, Transactions: txs, NoTxPool: true, - GasLimit: (*eth.Uint64Quantity)(&sysConfig.GasLimit), + GasLimit: (*eth.Uint64Quantity)(&gasLimit), Withdrawals: withdrawals, ParentBeaconBlockRoot: parentBeaconRoot, } diff --git a/op-node/rollup/derive/batches.go b/op-node/rollup/derive/batches.go index 4e3e20d1e50fc..398137e986076 100644 --- a/op-node/rollup/derive/batches.go +++ b/op-node/rollup/derive/batches.go @@ -133,6 +133,14 @@ func checkSingularBatch(cfg *rollup.Config, log log.Logger, l1Blocks []eth.L1Blo return BatchDrop } + // Future forks that contain upgrade transactions must be added here. + if (cfg.IsIsthmusActivationBlock(batch.Timestamp) || + cfg.IsInteropActivationBlock(batch.Timestamp)) && + len(batch.Transactions) > 0 { + log.Warn("dropping batch with user transactions in fork activation block") + return BatchDrop + } + spec := rollup.NewChainSpec(cfg) // Check if we ran out of sequencer time drift if max := batchOrigin.Time + spec.MaxSequencerDrift(batchOrigin.Time); batch.Timestamp > max { diff --git a/op-node/rollup/derive/batches_test.go b/op-node/rollup/derive/batches_test.go index a50773c71d754..177cc344ca4bb 100644 --- a/op-node/rollup/derive/batches_test.go +++ b/op-node/rollup/derive/batches_test.go @@ -56,6 +56,25 @@ func holoceneAt(t *uint64) func(*rollup.Config) { } } +func isthmusAt(t *uint64) func(*rollup.Config) { + return func(c *rollup.Config) { + c.DeltaTime = &zero64 + c.FjordTime = &zero64 + c.HoloceneTime = &zero64 + c.IsthmusTime = t + } +} + +func interopAt(t *uint64) func(*rollup.Config) { + return func(c *rollup.Config) { + c.DeltaTime = &zero64 + c.FjordTime = &zero64 + c.HoloceneTime = &zero64 + c.IsthmusTime = &zero64 + c.InteropTime = t + } +} + const defaultBlockTime = 2 func TestValidBatch(t *testing.T) { @@ -539,6 +558,42 @@ func TestValidBatch(t *testing.T) { }, Expected: BatchDrop, // dropped because it could have advanced the epoch to B }, + { + Name: "transactions in Isthmus upgrade block", + L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, + 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("invalid tx in isthmus upgrade block")}, + }, + }, + Expected: BatchDrop, + ExpectedLog: "dropping batch with user transactions in fork activation block", + ConfigMod: isthmusAt(&l2A1.Time), + }, + { + Name: "transactions in Interop upgrade block", + L1Blocks: []eth.L1BlockRef{l1A, l1B, l1C}, + 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("invalid tx in isthmus upgrade block")}, + }, + }, + Expected: BatchDrop, + ExpectedLog: "dropping batch with user transactions in fork activation block", + ConfigMod: interopAt(&l2A1.Time), + }, { Name: "empty tx included", L1Blocks: []eth.L1BlockRef{l1A, l1B}, @@ -630,6 +685,7 @@ func TestValidBatch(t *testing.T) { Expected: BatchDrop, }, } + spanBatchTestCases := []ValidBatchTestCase{ { Name: "missing L1 info", diff --git a/op-node/rollup/derive/isthmus_upgrade_transactions.go b/op-node/rollup/derive/isthmus_upgrade_transactions.go index 31c575af46994..cf80f0097241c 100644 --- a/op-node/rollup/derive/isthmus_upgrade_transactions.go +++ b/op-node/rollup/derive/isthmus_upgrade_transactions.go @@ -1,6 +1,7 @@ package derive import ( + "fmt" "math/big" "github.com/ethereum-optimism/optimism/op-service/predeploys" @@ -42,151 +43,118 @@ var ( blockHashDeploymentBytecode = common.FromHex("0x60538060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500") ) -func IsthmusNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { - upgradeTxns := make([]hexutil.Bytes, 0, 8) - - // Deploy L1 Block transaction - deployL1BlockTransaction, err := types.NewTx(&types.DepositTx{ - SourceHash: deployIsthmusL1BlockSource.SourceHash(), - From: L1BlockIsthmusDeployerAddress, - To: nil, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 425_000, - IsSystemTransaction: false, - Data: l1BlockIsthmusDeploymentBytecode, - }).MarshalBinary() - - if err != nil { - return nil, err - } - - upgradeTxns = append(upgradeTxns, deployL1BlockTransaction) - - // Deploy Gas Price Oracle transaction - deployGasPriceOracle, err := types.NewTx(&types.DepositTx{ - SourceHash: deployIsthmusGasPriceOracleSource.SourceHash(), - From: GasPriceOracleIsthmusDeployerAddress, - To: nil, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 1_625_000, - IsSystemTransaction: false, - Data: gasPriceOracleIsthmusDeploymentBytecode, - }).MarshalBinary() - - if err != nil { - return nil, err - } - - upgradeTxns = append(upgradeTxns, deployGasPriceOracle) - - // Deploy Operator Fee vault transaction - deployOperatorFeeVault, err := types.NewTx(&types.DepositTx{ - SourceHash: deployOperatorFeeVaultSource.SourceHash(), - From: OperatorFeeVaultDeployerAddress, - To: nil, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 500_000, - IsSystemTransaction: false, - Data: operatorFeeVaultDeploymentBytecode, - }).MarshalBinary() - - if err != nil { - return nil, err - } - - upgradeTxns = append(upgradeTxns, deployOperatorFeeVault) - - // Deploy L1 Block Proxy upgrade transaction - updateL1BlockProxy, err := types.NewTx(&types.DepositTx{ - SourceHash: updateIsthmusL1BlockProxySource.SourceHash(), - From: common.Address{}, - To: &predeploys.L1BlockAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 50_000, - IsSystemTransaction: false, - Data: upgradeToCalldata(isthmusL1BlockAddress), - }).MarshalBinary() - - if err != nil { - return nil, err +func IsthmusNetworkUpgradeTransactions() ([]hexutil.Bytes, uint64, error) { + upgradeTxns := []*types.DepositTx{ + // Deploy L1 Block transaction + { + SourceHash: deployIsthmusL1BlockSource.SourceHash(), + From: L1BlockIsthmusDeployerAddress, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 425_000, + IsSystemTransaction: false, + Data: l1BlockIsthmusDeploymentBytecode, + }, + // Deploy Gas Price Oracle transaction + { + SourceHash: deployIsthmusGasPriceOracleSource.SourceHash(), + From: GasPriceOracleIsthmusDeployerAddress, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 1_625_000, + IsSystemTransaction: false, + Data: gasPriceOracleIsthmusDeploymentBytecode, + }, + // Deploy Operator Fee vault transaction + { + SourceHash: deployOperatorFeeVaultSource.SourceHash(), + From: OperatorFeeVaultDeployerAddress, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 500_000, + IsSystemTransaction: false, + Data: operatorFeeVaultDeploymentBytecode, + }, + // Deploy L1 Block Proxy upgrade transaction + { + SourceHash: updateIsthmusL1BlockProxySource.SourceHash(), + From: common.Address{}, + To: &predeploys.L1BlockAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 50_000, + IsSystemTransaction: false, + Data: upgradeToCalldata(isthmusL1BlockAddress), + }, + // Deploy Gas Price Oracle Proxy upgrade transaction + { + SourceHash: updateIsthmusGasPriceOracleSource.SourceHash(), + From: common.Address{}, + To: &predeploys.GasPriceOracleAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 50_000, + IsSystemTransaction: false, + Data: upgradeToCalldata(isthmusGasPriceOracleAddress), + }, + // Deploy Operator Fee Vault Proxy upgrade transaction + { + SourceHash: updateOperatorFeeVaultSource.SourceHash(), + From: common.Address{}, + To: &predeploys.OperatorFeeVaultAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 50_000, + IsSystemTransaction: false, + Data: upgradeToCalldata(OperatorFeeVaultAddress), + }, + // Enable Isthmus transaction + { + SourceHash: enableIsthmusSource.SourceHash(), + From: L1InfoDepositerAddress, + To: &predeploys.GasPriceOracleAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 90_000, + IsSystemTransaction: false, + Data: enableIsthmusInput, + }, + // Deploy Historical Block Hashes contract + { + SourceHash: blockHashDeployerSource.SourceHash(), + From: predeploys.EIP2935ContractDeployer, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 250_000, + IsSystemTransaction: false, + Data: blockHashDeploymentBytecode, + }, } - - upgradeTxns = append(upgradeTxns, updateL1BlockProxy) - - // Deploy Gas Price Oracle Proxy upgrade transaction - updateGasPriceOracleProxy, err := types.NewTx(&types.DepositTx{ - SourceHash: updateIsthmusGasPriceOracleSource.SourceHash(), - From: common.Address{}, - To: &predeploys.GasPriceOracleAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 50_000, - IsSystemTransaction: false, - Data: upgradeToCalldata(isthmusGasPriceOracleAddress), - }).MarshalBinary() - + encodedTxs, err := marshalBinaryDepositTxs(upgradeTxns...) if err != nil { - return nil, err + return nil, 0, err } - upgradeTxns = append(upgradeTxns, updateGasPriceOracleProxy) - - // Deploy Operator Fee Vault Proxy upgrade transaction - updateOperatorFeeVaultProxy, err := types.NewTx(&types.DepositTx{ - SourceHash: updateOperatorFeeVaultSource.SourceHash(), - From: common.Address{}, - To: &predeploys.OperatorFeeVaultAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 50_000, - IsSystemTransaction: false, - Data: upgradeToCalldata(OperatorFeeVaultAddress), - }).MarshalBinary() - - if err != nil { - return nil, err + var totalGas uint64 + for _, tx := range upgradeTxns { + totalGas += tx.Gas } - upgradeTxns = append(upgradeTxns, updateOperatorFeeVaultProxy) - - // Enable Isthmus transaction - enableIsthmus, err := types.NewTx(&types.DepositTx{ - SourceHash: enableIsthmusSource.SourceHash(), - From: L1InfoDepositerAddress, - To: &predeploys.GasPriceOracleAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 90_000, - IsSystemTransaction: false, - Data: enableIsthmusInput, - }).MarshalBinary() - - if err != nil { - return nil, err - } - - upgradeTxns = append(upgradeTxns, enableIsthmus) - - deployHistoricalBlockHashesContract, err := types.NewTx(&types.DepositTx{ - SourceHash: blockHashDeployerSource.SourceHash(), - From: predeploys.EIP2935ContractDeployer, - To: nil, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 250_000, - IsSystemTransaction: false, - Data: blockHashDeploymentBytecode, - }).MarshalBinary() + return encodedTxs, totalGas, nil +} - if err != nil { - return nil, err +func marshalBinaryDepositTxs(txs ...*types.DepositTx) ([]hexutil.Bytes, error) { + encodedTxs := make([]hexutil.Bytes, 0, len(txs)) + for i, tx := range txs { + encodedTx, err := types.NewTx(tx).MarshalBinary() + if err != nil { + return nil, fmt.Errorf("marshaling deposit transaction %d: %w", i, err) + } + encodedTxs = append(encodedTxs, encodedTx) } - - upgradeTxns = append(upgradeTxns, deployHistoricalBlockHashesContract) - - return upgradeTxns, nil + return encodedTxs, nil } diff --git a/op-node/rollup/derive/isthmus_upgrade_transactions_test.go b/op-node/rollup/derive/isthmus_upgrade_transactions_test.go index c005d57b16130..021ff820677ed 100644 --- a/op-node/rollup/derive/isthmus_upgrade_transactions_test.go +++ b/op-node/rollup/derive/isthmus_upgrade_transactions_test.go @@ -51,9 +51,15 @@ func TestIsthmusSourcesMatchSpec(t *testing.T) { } func TestIsthmusNetworkTransactions(t *testing.T) { - upgradeTxns, err := IsthmusNetworkUpgradeTransactions() + upgradeTxns, upgradeGas, err := IsthmusNetworkUpgradeTransactions() require.NoError(t, err) require.Len(t, upgradeTxns, 8) + var gas uint64 + for _, txb := range upgradeTxns { + _, tx := toDepositTxn(t, txb) + gas += tx.Gas() + } + require.Equal(t, gas, upgradeGas) deployL1BlockSender, deployL1Block := toDepositTxn(t, upgradeTxns[0]) require.Equal(t, deployL1BlockSender, common.HexToAddress("0x4210000000000000000000000000000000000003")) diff --git a/op-node/rollup/sequencing/sequencer.go b/op-node/rollup/sequencing/sequencer.go index d66bcbad02248..5e8d806637255 100644 --- a/op-node/rollup/sequencing/sequencer.go +++ b/op-node/rollup/sequencing/sequencer.go @@ -545,11 +545,23 @@ func (d *Sequencer) startBuildingBlock() { d.log.Info("Sequencing Fjord upgrade block") } - // For the Granite activation block we shouldn't include any sequencer transactions. + // For the Granite activation block we can include sequencer transactions. if d.rollupCfg.IsGraniteActivationBlock(uint64(attrs.Timestamp)) { d.log.Info("Sequencing Granite upgrade block") } + // For the Isthmus activation block we must not include any sequencer transactions. + if d.rollupCfg.IsIsthmusActivationBlock(uint64(attrs.Timestamp)) { + attrs.NoTxPool = true + d.log.Info("Sequencing Isthmus upgrade block") + } + + // For the Interop activation block we must not include any sequencer transactions. + if d.rollupCfg.IsInteropActivationBlock(uint64(attrs.Timestamp)) { + attrs.NoTxPool = true + d.log.Info("Sequencing Interop upgrade block") + } + d.log.Debug("prepared attributes for new block", "num", l2Head.Number+1, "time", uint64(attrs.Timestamp), "origin", l1Origin, "origin_time", l1Origin.Time, "noTxPool", attrs.NoTxPool) diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 2726fd6b4db7c..09242cb542c12 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -508,11 +508,17 @@ func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool { // IsActivationBlock returns the fork which activates at the block with time newTime if the previous // block's time is oldTime. It return an empty ForkName if no fork activation takes place between // those timestamps. It can be used for both, L1 and L2 blocks. -// TODO(12490): Currently only supports Holocene. Will be modularized in a follow-up. +// TODO(14756): Currently only supports Holocene, Isthmus, Interop. Will be modularized in a follow-up. func (c *Config) IsActivationBlock(oldTime, newTime uint64) ForkName { if c.IsHolocene(newTime) && !c.IsHolocene(oldTime) { return Holocene } + if c.IsIsthmus(newTime) && !c.IsIsthmus(oldTime) { + return Isthmus + } + if c.IsInterop(newTime) && !c.IsInterop(oldTime) { + return Interop + } return "" }