|
| 1 | +package actions |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "encoding/hex" |
| 6 | + "math/big" |
| 7 | + "testing" |
| 8 | + |
| 9 | + "github.com/ethereum-optimism/optimism/op-service/predeploys" |
| 10 | + "github.com/ethereum/go-ethereum/accounts/abi/bind" |
| 11 | + "github.com/ethereum/go-ethereum/common" |
| 12 | + "github.com/ethereum/go-ethereum/common/hexutil" |
| 13 | + "github.com/ethereum/go-ethereum/core/types" |
| 14 | + "github.com/ethereum/go-ethereum/crypto" |
| 15 | + "github.com/ethereum/go-ethereum/log" |
| 16 | + "github.com/stretchr/testify/require" |
| 17 | + |
| 18 | + "github.com/ethereum-optimism/optimism/op-bindings/bindings" |
| 19 | + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" |
| 20 | + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" |
| 21 | + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" |
| 22 | + "github.com/ethereum-optimism/optimism/op-service/testlog" |
| 23 | +) |
| 24 | + |
| 25 | +var ( |
| 26 | + fjordGasPriceOracleCodeHash = common.HexToHash("0xa88fa50a2745b15e6794247614b5298483070661adacb8d32d716434ed24c6b2") |
| 27 | + // https://basescan.org/tx/0x8debb2fe54200183fb8baa3c6dbd8e6ec2e4f7a4add87416cd60336b8326d16a |
| 28 | + txHex = "02f875822105819b8405709fb884057d460082e97f94273ca93a52b817294830ed7572aa591ccfa647fd80881249c58b0021fb3fc080a05bb08ccfd68f83392e446dac64d88a2d28e7072c06502dfabc4a77e77b5c7913a05878d53dd4ebba4f6367e572d524dffcabeec3abb1d8725ee3ac5dc32e1852e3" |
| 29 | +) |
| 30 | + |
| 31 | +func TestFjordNetworkUpgradeTransactions(gt *testing.T) { |
| 32 | + t := NewDefaultTesting(gt) |
| 33 | + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) |
| 34 | + genesisBlock := hexutil.Uint64(0) |
| 35 | + fjordOffset := hexutil.Uint64(2) |
| 36 | + |
| 37 | + dp.DeployConfig.L1CancunTimeOffset = &genesisBlock // can be removed once Cancun on L1 is the default |
| 38 | + |
| 39 | + // Activate all forks at genesis, and schedule Fjord the block after |
| 40 | + dp.DeployConfig.L2GenesisRegolithTimeOffset = &genesisBlock |
| 41 | + dp.DeployConfig.L2GenesisCanyonTimeOffset = &genesisBlock |
| 42 | + dp.DeployConfig.L2GenesisDeltaTimeOffset = &genesisBlock |
| 43 | + dp.DeployConfig.L2GenesisEcotoneTimeOffset = &genesisBlock |
| 44 | + dp.DeployConfig.L2GenesisFjordTimeOffset = &fjordOffset |
| 45 | + require.NoError(t, dp.DeployConfig.Check(), "must have valid config") |
| 46 | + |
| 47 | + sd := e2eutils.Setup(t, dp, defaultAlloc) |
| 48 | + log := testlog.Logger(t, log.LvlDebug) |
| 49 | + _, _, _, sequencer, engine, verifier, _, _ := setupReorgTestActors(t, dp, sd, log) |
| 50 | + ethCl := engine.EthClient() |
| 51 | + |
| 52 | + // start op-nodes |
| 53 | + sequencer.ActL2PipelineFull(t) |
| 54 | + verifier.ActL2PipelineFull(t) |
| 55 | + |
| 56 | + // Get gas price from oracle |
| 57 | + gasPriceOracle, err := bindings.NewGasPriceOracleCaller(predeploys.GasPriceOracleAddr, ethCl) |
| 58 | + require.NoError(t, err) |
| 59 | + |
| 60 | + // Get current implementations addresses (by slot) for L1Block + GasPriceOracle |
| 61 | + initialGasPriceOracleAddress, err := ethCl.StorageAt(context.Background(), predeploys.GasPriceOracleAddr, genesis.ImplementationSlot, nil) |
| 62 | + require.NoError(t, err) |
| 63 | + |
| 64 | + sequencer.ActBuildL2ToFjord(t) |
| 65 | + |
| 66 | + // get latest block |
| 67 | + latestBlock, err := ethCl.BlockByNumber(context.Background(), nil) |
| 68 | + require.NoError(t, err) |
| 69 | + require.Equal(t, sequencer.L2Unsafe().Number, latestBlock.Number().Uint64()) |
| 70 | + |
| 71 | + transactions := latestBlock.Transactions() |
| 72 | + // L1Block: 1 set-L1-info + 1 deploys + 1 upgradeTo + 1 enable fjord on GPO |
| 73 | + // See [derive.FjordNetworkUpgradeTransactions] |
| 74 | + require.Equal(t, 4, len(transactions)) |
| 75 | + |
| 76 | + // All transactions are successful |
| 77 | + for i := 1; i < 4; i++ { |
| 78 | + txn := transactions[i] |
| 79 | + receipt, err := ethCl.TransactionReceipt(context.Background(), txn.Hash()) |
| 80 | + require.NoError(t, err) |
| 81 | + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) |
| 82 | + require.NotEmpty(t, txn.Data(), "upgrade tx must provide input data") |
| 83 | + } |
| 84 | + |
| 85 | + expectedGasPriceOracleAddress := crypto.CreateAddress(derive.GasPriceOracleFjordDeployerAddress, 0) |
| 86 | + |
| 87 | + // Gas Price Oracle Proxy is updated |
| 88 | + updatedGasPriceOracleAddress, err := ethCl.StorageAt(context.Background(), predeploys.GasPriceOracleAddr, genesis.ImplementationSlot, latestBlock.Number()) |
| 89 | + require.NoError(t, err) |
| 90 | + require.Equal(t, expectedGasPriceOracleAddress, common.BytesToAddress(updatedGasPriceOracleAddress)) |
| 91 | + require.NotEqualf(t, initialGasPriceOracleAddress, updatedGasPriceOracleAddress, "Gas Price Oracle Proxy address should have changed") |
| 92 | + verifyCodeHashMatches(t, ethCl, expectedGasPriceOracleAddress, fjordGasPriceOracleCodeHash) |
| 93 | + |
| 94 | + // Check that Fjord was activated |
| 95 | + isFjord, err := gasPriceOracle.IsFjord(nil) |
| 96 | + require.NoError(t, err) |
| 97 | + require.True(t, isFjord) |
| 98 | + |
| 99 | + // Check GetL1GasUsed is updated |
| 100 | + txData, err := hex.DecodeString(txHex) |
| 101 | + require.NoError(t, err) |
| 102 | + |
| 103 | + gpoL1GasUsed, err := gasPriceOracle.GetL1GasUsed(&bind.CallOpts{}, txData) |
| 104 | + require.NoError(t, err) |
| 105 | + require.Equal(gt, uint64(1_888), gpoL1GasUsed.Uint64()) |
| 106 | + |
| 107 | + // Check that GetL1Fee takes into account fast LZ |
| 108 | + gpoFee, err := gasPriceOracle.GetL1Fee(&bind.CallOpts{}, txData) |
| 109 | + require.NoError(t, err) |
| 110 | + |
| 111 | + gethFee := fjordL1Cost(t, gasPriceOracle, types.RollupCostData{ |
| 112 | + FastLzSize: uint64(types.FlzCompressLen(txData) + 68), |
| 113 | + }) |
| 114 | + require.Equal(t, gethFee.Uint64(), gpoFee.Uint64()) |
| 115 | + |
| 116 | + // Check that L1FeeUpperBound works |
| 117 | + upperBound, err := gasPriceOracle.GetL1FeeUpperBound(&bind.CallOpts{}, big.NewInt(int64(len(txData)))) |
| 118 | + require.NoError(t, err) |
| 119 | + |
| 120 | + txLen := len(txData) + 68 |
| 121 | + flzUpperBound := uint64(txLen + txLen/255 + 16) |
| 122 | + |
| 123 | + upperBoundCost := fjordL1Cost(t, gasPriceOracle, types.RollupCostData{FastLzSize: flzUpperBound}) |
| 124 | + require.Equal(t, upperBoundCost.Uint64(), upperBound.Uint64()) |
| 125 | +} |
| 126 | + |
| 127 | +func fjordL1Cost(t require.TestingT, gasPriceOracle *bindings.GasPriceOracleCaller, rollupCostData types.RollupCostData) *big.Int { |
| 128 | + baseFeeScalar, err := gasPriceOracle.BaseFeeScalar(nil) |
| 129 | + require.NoError(t, err) |
| 130 | + l1BaseFee, err := gasPriceOracle.L1BaseFee(nil) |
| 131 | + require.NoError(t, err) |
| 132 | + blobBaseFeeScalar, err := gasPriceOracle.BlobBaseFeeScalar(nil) |
| 133 | + require.NoError(t, err) |
| 134 | + blobBaseFee, err := gasPriceOracle.BlobBaseFee(nil) |
| 135 | + require.NoError(t, err) |
| 136 | + |
| 137 | + costFunc := types.NewL1CostFuncFjord( |
| 138 | + l1BaseFee, |
| 139 | + blobBaseFee, |
| 140 | + new(big.Int).SetUint64(uint64(baseFeeScalar)), |
| 141 | + new(big.Int).SetUint64(uint64(blobBaseFeeScalar))) |
| 142 | + |
| 143 | + fee, _ := costFunc(rollupCostData) |
| 144 | + return fee |
| 145 | +} |
0 commit comments