From 99df6a532ffa03806b8ef9fe0ecebf0f5d3ba549 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Sun, 22 Sep 2024 11:19:16 -0700 Subject: [PATCH] handle Holocene l1 attributes in l1 data fee computation --- core/types/rollup_cost.go | 19 ++++++--- core/types/rollup_cost_test.go | 71 +++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/core/types/rollup_cost.go b/core/types/rollup_cost.go index e0bc26d92f..c5993b7f05 100644 --- a/core/types/rollup_cost.go +++ b/core/types/rollup_cost.go @@ -54,6 +54,8 @@ var ( BedrockL1AttributesSelector = []byte{0x01, 0x5d, 0x8e, 0xb9} // EcotoneL1AttributesSelector is the selector indicating Ecotone style L1 gas attributes. EcotoneL1AttributesSelector = []byte{0x44, 0x0a, 0x5e, 0x20} + // HoloceneL1AttributesSelector is the selector indicating Holocene style L1 gas attributes. + HoloceneL1AttributesSelector = []byte{0xd1, 0xfb, 0xe1, 0x5b} // L1BlockAddr is the address of the L1Block contract which stores the L1 gas attributes. L1BlockAddr = common.HexToAddress("0x4200000000000000000000000000000000000015") @@ -264,7 +266,7 @@ func extractL1GasParams(config *params.ChainConfig, time uint64, data []byte) (g // If so, fall through to the pre-ecotone format // Both Ecotone and Fjord use the same function selector if config.IsEcotone(time) && len(data) >= 4 && !bytes.Equal(data[0:4], BedrockL1AttributesSelector) { - p, err := extractL1GasParamsPostEcotone(data) + p, err := extractL1GasParamsPostEcotone(config.IsHolocene(time), data) if err != nil { return gasParams{}, err } @@ -309,13 +311,16 @@ func extractL1GasParamsPreEcotone(config *params.ChainConfig, time uint64, data } // extractL1GasParamsPostEcotone extracts the gas parameters necessary to compute gas from L1 attribute -// info calldata after the Ecotone upgrade, but not for the very first Ecotone block. -func extractL1GasParamsPostEcotone(data []byte) (gasParams, error) { - if len(data) != 164 { +// info calldata after the Ecotone upgrade, other than the very first Ecotone block. +func extractL1GasParamsPostEcotone(isHolocene bool, data []byte) (gasParams, error) { + if isHolocene && len(data) >= 4 && bytes.Equal(data[0:4], HoloceneL1AttributesSelector) { + if len(data) != 180 { + return gasParams{}, fmt.Errorf("expected 180 L1 info bytes, got %d", len(data)) + } + } else if len(data) != 164 { return gasParams{}, fmt.Errorf("expected 164 L1 info bytes, got %d", len(data)) } // data layout assumed for Ecotone: - // offset type varname // 0 // 4 uint32 _basefeeScalar // 8 uint32 _blobBaseFeeScalar @@ -326,6 +331,10 @@ func extractL1GasParamsPostEcotone(data []byte) (gasParams, error) { // 68 uint256 _blobBaseFee, // 100 bytes32 _hash, // 132 bytes32 _batcherHash, + // + // added by Holocene: + // 164 uint64 _eip1559Denominator, + // 172 uint64 _eip1559Elasticity, l1BaseFee := new(big.Int).SetBytes(data[36:68]) l1BlobBaseFee := new(big.Int).SetBytes(data[68:100]) l1BaseFeeScalar := binary.BigEndian.Uint32(data[4:8]) diff --git a/core/types/rollup_cost_test.go b/core/types/rollup_cost_test.go index 8d0f1e9218..a024d0c871 100644 --- a/core/types/rollup_cost_test.go +++ b/core/types/rollup_cost_test.go @@ -174,7 +174,69 @@ func TestExtractEcotoneGasParams(t *testing.T) { // make sure wrong amont of data results in error data = append(data, 0x00) // tack on garbage byte - _, err = extractL1GasParamsPostEcotone(data) + _, err = extractL1GasParamsPostEcotone(false, data) + require.Error(t, err) + + // make sure holocene attributes result in error prior to Holocene activation + data = getHoloceneL1Attributes( + baseFee, + blobBaseFee, + baseFeeScalar, + blobBaseFeeScalar, + ) + gasparams, err = extractL1GasParams(config, zeroTime, data) + require.Error(t, err) +} + +func TestExtractHoloceneGasParams(t *testing.T) { + zeroTime := uint64(0) + // create a config where holocene upgrade is active + config := ¶ms.ChainConfig{ + Optimism: params.OptimismTestConfig.Optimism, + RegolithTime: &zeroTime, + EcotoneTime: &zeroTime, + HoloceneTime: &zeroTime, + } + require.True(t, config.IsOptimismEcotone(zeroTime)) + require.True(t, config.IsOptimismHolocene(zeroTime)) + + // make sure empty attributes returns error + data := []byte{} + _, err := extractL1GasParamsPostEcotone(true, data) + require.Error(t, err) + + // Check that we still allow Ecotone-style L1 attributes post-Holocene, since the very first Holocene block will + // have Ecotone attributes. + data = getEcotoneL1Attributes( + baseFee, + blobBaseFee, + baseFeeScalar, + blobBaseFeeScalar, + ) + gasparams, err := extractL1GasParams(config, zeroTime, data) + require.NoError(t, err) + costFunc := gasparams.costFunc + c, g := costFunc(emptyTx.RollupCostData()) + require.Equal(t, ecotoneGas, g) + require.Equal(t, ecotoneFee, c) + + // Now confirm Holocene-style L1 attributes work. + data = getHoloceneL1Attributes( + baseFee, + blobBaseFee, + baseFeeScalar, + blobBaseFeeScalar, + ) + gasparams, err = extractL1GasParams(config, zeroTime, data) + require.NoError(t, err) + costFunc = gasparams.costFunc + c, g = costFunc(emptyTx.RollupCostData()) + require.Equal(t, ecotoneGas, g) + require.Equal(t, ecotoneFee, c) + + // make sure wrong amont of data results in error + data = append(data, 0x00) // tack on garbage byte + _, err = extractL1GasParamsPostEcotone(true, data) require.Error(t, err) } @@ -263,6 +325,13 @@ func getEcotoneL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScal return data } +func getHoloceneL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScalar *big.Int) []byte { + data := getEcotoneL1Attributes(baseFee, blobBaseFee, baseFeeScalar, blobBaseFeeScalar) + copy(data, HoloceneL1AttributesSelector) + data = append(data, make([]byte, 16)...) // add 0 values for the two new attributes + return data +} + type testStateGetter struct { baseFee, blobBaseFee, overhead, scalar *big.Int baseFeeScalar, blobBaseFeeScalar uint32