From 866d7f27a9b6e62fd0af7e0540bb1bc42119f75b Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Thu, 27 Feb 2025 19:51:40 +0300 Subject: [PATCH 1/6] use fee configs in tests --- plugin/evm/header/block_gas_cost_test.go | 73 ++++++++++++------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/plugin/evm/header/block_gas_cost_test.go b/plugin/evm/header/block_gas_cost_test.go index e221f33d88..71f8ecea8d 100644 --- a/plugin/evm/header/block_gas_cost_test.go +++ b/plugin/evm/header/block_gas_cost_test.go @@ -19,23 +19,26 @@ func TestBlockGasCost(t *testing.T) { TargetBlockRate: 2, BlockGasCostStep: big.NewInt(50_000), } - BlockGasCostTest(t, testFeeConfig) - + t.Run("normal", func(t *testing.T) { + BlockGasCostTest(t, testFeeConfig) + }) testFeeConfigDouble := commontype.FeeConfig{ MinBlockGasCost: big.NewInt(2), MaxBlockGasCost: big.NewInt(2_000_000), TargetBlockRate: 4, BlockGasCostStep: big.NewInt(100_000), } - BlockGasCostTest(t, testFeeConfigDouble) + t.Run("double", func(t *testing.T) { + BlockGasCostTest(t, testFeeConfigDouble) + }) } -func BlockGasCostTest(t *testing.T, testFeeConfig commontype.FeeConfig) { - maxBlockGasCostBig := testFeeConfig.MaxBlockGasCost - maxBlockGasCost := testFeeConfig.MaxBlockGasCost.Uint64() - blockGasCostStep := testFeeConfig.BlockGasCostStep.Uint64() - minBlockGasCost := testFeeConfig.MinBlockGasCost.Uint64() - targetBlockRate := testFeeConfig.TargetBlockRate +func BlockGasCostTest(t *testing.T, feeConfig commontype.FeeConfig) { + maxBlockGasCostBig := feeConfig.MaxBlockGasCost + maxBlockGasCost := feeConfig.MaxBlockGasCost.Uint64() + blockGasCostStep := feeConfig.BlockGasCostStep.Uint64() + minBlockGasCost := feeConfig.MinBlockGasCost.Uint64() + targetBlockRate := feeConfig.TargetBlockRate tests := []struct { name string @@ -54,7 +57,7 @@ func BlockGasCostTest(t *testing.T, testFeeConfig commontype.FeeConfig) { { name: "negative_time_elapsed", parentTime: 10, - parentCost: maxBlockGasCostBig, + parentCost: feeConfig.MinBlockGasCost, timestamp: 9, expected: minBlockGasCost + blockGasCostStep*targetBlockRate, }, @@ -68,7 +71,7 @@ func BlockGasCostTest(t *testing.T, testFeeConfig commontype.FeeConfig) { } assert.Equal(t, test.expected, BlockGasCost( - testFeeConfig, + feeConfig, parent, test.timestamp, )) @@ -83,22 +86,26 @@ func TestBlockGasCostWithStep(t *testing.T) { TargetBlockRate: 2, BlockGasCostStep: big.NewInt(50_000), } - BlockGasCostWithStepTest(t, testFeeConfig) + t.Run("normal", func(t *testing.T) { + BlockGasCostWithStepTest(t, testFeeConfig) + }) testFeeConfigDouble := commontype.FeeConfig{ - MinBlockGasCost: big.NewInt(2), + MinBlockGasCost: big.NewInt(200_000), MaxBlockGasCost: big.NewInt(2_000_000), TargetBlockRate: 4, BlockGasCostStep: big.NewInt(100_000), } - BlockGasCostWithStepTest(t, testFeeConfigDouble) + t.Run("double", func(t *testing.T) { + BlockGasCostWithStepTest(t, testFeeConfigDouble) + }) } -func BlockGasCostWithStepTest(t *testing.T, testFeeConfig commontype.FeeConfig) { - minBlockGasCost := testFeeConfig.MinBlockGasCost.Uint64() - blockGasCostStep := testFeeConfig.BlockGasCostStep.Uint64() - targetBlockRate := testFeeConfig.TargetBlockRate - bigMaxBlockGasCost := testFeeConfig.MaxBlockGasCost +func BlockGasCostWithStepTest(t *testing.T, feeConfig commontype.FeeConfig) { + minBlockGasCost := feeConfig.MinBlockGasCost.Uint64() + blockGasCostStep := feeConfig.BlockGasCostStep.Uint64() + targetBlockRate := feeConfig.TargetBlockRate + bigMaxBlockGasCost := feeConfig.MaxBlockGasCost maxBlockGasCost := bigMaxBlockGasCost.Uint64() tests := []struct { name string @@ -149,41 +156,35 @@ func BlockGasCostWithStepTest(t *testing.T, testFeeConfig commontype.FeeConfig) expected: 900_000, }, { - name: "timeElapsed_over_target_3", + name: "timeElapsed_over_target_1", parentCost: bigMaxBlockGasCost, - timeElapsed: 3, - expected: maxBlockGasCost - (3-targetBlockRate)*blockGasCostStep, + timeElapsed: targetBlockRate + 1, + expected: maxBlockGasCost - blockGasCostStep, }, { name: "timeElapsed_over_target_10", parentCost: bigMaxBlockGasCost, - timeElapsed: 10, - expected: maxBlockGasCost - (10-targetBlockRate)*blockGasCostStep, + timeElapsed: targetBlockRate + 10, + expected: maxBlockGasCost - 10*blockGasCostStep, }, { - name: "timeElapsed_over_target_20", + name: "timeElapsed_over_target_15", parentCost: bigMaxBlockGasCost, - timeElapsed: 20, - expected: maxBlockGasCost - (20-targetBlockRate)*blockGasCostStep, - }, - { - name: "timeElapsed_over_target_22", - parentCost: bigMaxBlockGasCost, - timeElapsed: 22, - expected: maxBlockGasCost - (22-targetBlockRate)*blockGasCostStep, + timeElapsed: targetBlockRate + 15, + expected: maxBlockGasCost - 15*blockGasCostStep, }, { name: "timeElapsed_large_clamped_to_0", parentCost: bigMaxBlockGasCost, - timeElapsed: 23, - expected: 0, + timeElapsed: targetBlockRate + 100, + expected: minBlockGasCost, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert.Equal(t, test.expected, BlockGasCostWithStep( - testFeeConfig, + feeConfig, test.parentCost, blockGasCostStep, test.timeElapsed, From 5fc218f51160e2c3eee4e5960af5a540ce43b5b3 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Wed, 26 Feb 2025 23:45:31 +0300 Subject: [PATCH 2/6] update latest json --- plugin/evm/vm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 8815e50ce1..3b7210325e 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -98,7 +98,7 @@ var ( genesisJSONDurango = genesisJSON(params.TestDurangoChainConfig) genesisJSONEtna = genesisJSON(params.TestEtnaChainConfig) genesisJSONFUpgrade = genesisJSON(params.TestFUpgradeChainConfig) - genesisJSONLatest = genesisJSONEtna + genesisJSONLatest = genesisJSONFUpgrade ) func init() { From 3073c3cbc3915ab0cc2bf9f983c8b4229d3a4d37 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 18 Feb 2025 11:40:51 -0500 Subject: [PATCH 3/6] Split `CalcExtraData` out of `CalcBaseFee` (#802) --- cmd/evm/internal/t8ntool/transition.go | 6 +- consensus/dummy/consensus.go | 12 ++- consensus/dummy/dynamic_fees.go | 125 ++++++++++++++++++------- consensus/dummy/dynamic_fees_test.go | 7 +- core/chain_makers.go | 7 +- core/state_processor_test.go | 13 +-- core/txpool/blobpool/blobpool_test.go | 16 ++-- miner/worker.go | 6 +- 8 files changed, 130 insertions(+), 62 deletions(-) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index d2d957875d..010b0cf95a 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -90,7 +90,7 @@ type input struct { } func Transition(ctx *cli.Context) error { - var getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { return nil, nil } + getTracer := func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { return nil, nil } baseDir, err := createBasedir(ctx) if err != nil { @@ -230,7 +230,7 @@ func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error { feeConfig.MinBaseFee = env.MinBaseFee } var err error - _, env.BaseFee, err = dummy.CalcBaseFee(chainConfig, feeConfig, parent, env.Timestamp) + env.BaseFee, err = dummy.CalcBaseFee(chainConfig, feeConfig, parent, env.Timestamp) if err != nil { return NewError(ErrorConfig, fmt.Errorf("failed calculating base fee: %v", err)) } @@ -282,7 +282,7 @@ func saveFile(baseDir, filename string, data interface{}) error { return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err)) } location := path.Join(baseDir, filename) - if err = os.WriteFile(location, b, 0644); err != nil { + if err = os.WriteFile(location, b, 0o644); err != nil { return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err)) } log.Info("Wrote file", "file", location) diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index 15e5de4aef..8626419538 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -157,14 +157,20 @@ func (eng *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header return nil } - // Verify header.Extra and header.BaseFee match their expected values. - expectedExtraPrefix, expectedBaseFee, err := CalcBaseFee(config, feeConfig, parent, header.Time) + // Verify header.Extra matches the expected value. + expectedExtraPrefix, err := CalcExtraPrefix(config, feeConfig, parent, header.Time) if err != nil { - return fmt.Errorf("failed to calculate base fee: %w", err) + return fmt.Errorf("failed to calculate extra prefix: %w", err) } if !bytes.HasPrefix(header.Extra, expectedExtraPrefix) { return fmt.Errorf("expected header.Extra to have prefix: %x, found %x", expectedExtraPrefix, header.Extra) } + + // Verify header.BaseFee matches the expected value. + expectedBaseFee, err := CalcBaseFee(config, feeConfig, parent, header.Time) + if err != nil { + return fmt.Errorf("failed to calculate base fee: %w", err) + } if !utils.BigEqual(header.BaseFee, expectedBaseFee) { return fmt.Errorf("expected base fee (%d), found (%d)", expectedBaseFee, header.BaseFee) } diff --git a/consensus/dummy/dynamic_fees.go b/consensus/dummy/dynamic_fees.go index e60078d46e..571409db91 100644 --- a/consensus/dummy/dynamic_fees.go +++ b/consensus/dummy/dynamic_fees.go @@ -21,51 +21,51 @@ var ( errEstimateBaseFeeWithoutActivation = errors.New("cannot estimate base fee for chain without activation") ) +// CalcExtraPrefix takes the previous header and the timestamp of its child +// block and calculates the expected extra prefix for the child block. +// +// CalcExtraPrefix should only be called if timestamp >= config.ApricotPhase3Timestamp +func CalcExtraPrefix( + config *params.ChainConfig, + feeConfig commontype.FeeConfig, + parent *types.Header, + timestamp uint64, +) ([]byte, error) { + window, err := calcFeeWindow(config, feeConfig, parent, timestamp) + return window.Bytes(), err +} + // CalcBaseFee takes the previous header and the timestamp of its child block -// and calculates the expected base fee as well as the encoding of the past -// pricing information for the child block. -func CalcBaseFee(config *params.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) ([]byte, *big.Int, error) { +// and calculates the expected base fee for the child block. +// +// CalcBaseFee should only be called if timestamp >= config.IsSubnetEVMTimestamp +func CalcBaseFee(config *params.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) (*big.Int, error) { // If the current block is the first EIP-1559 block, or it is the genesis block // return the initial slice and initial base fee. - isSubnetEVM := config.IsSubnetEVM(parent.Time) - if !isSubnetEVM || parent.Number.Cmp(common.Big0) == 0 { - initialSlice := (&DynamicFeeWindow{}).Bytes() - return initialSlice, feeConfig.MinBaseFee, nil + if !config.IsSubnetEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 { + return big.NewInt(feeConfig.MinBaseFee.Int64()), nil } - dynamicFeeWindow, err := ParseDynamicFeeWindow(parent.Extra) + dynamicFeeWindow, err := calcFeeWindow(config, feeConfig, parent, timestamp) if err != nil { - return nil, nil, err + return nil, err } - if timestamp < parent.Time { - return nil, nil, fmt.Errorf("cannot calculate base fee for timestamp %d prior to parent timestamp %d", timestamp, parent.Time) - } - timeElapsed := timestamp - parent.Time - var ( baseFee = new(big.Int).Set(parent.BaseFee) baseFeeChangeDenominator = feeConfig.BaseFeeChangeDenominator - parentGasTargetBig = feeConfig.TargetGas - parentGasTarget = parentGasTargetBig.Uint64() + parentGasTarget = feeConfig.TargetGas.Uint64() + totalGas = dynamicFeeWindow.Sum() ) - // Compute the new state of the gas rolling window. - dynamicFeeWindow.Add(parent.GasUsed) - - // roll the window over by the timeElapsed to generate the new rollup - // window. - dynamicFeeWindow.Shift(timeElapsed) - dynamicFeeWindowBytes := dynamicFeeWindow.Bytes() - - // Calculate the amount of gas consumed within the rollup window. - totalGas := dynamicFeeWindow.Sum() if totalGas == parentGasTarget { - return dynamicFeeWindowBytes, baseFee, nil + return baseFee, nil } - num := new(big.Int) - + var ( + num = new(big.Int) + parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget) + ) if totalGas > parentGasTarget { // If the parent block used more gas than its target, the baseFee should increase. num.SetUint64(totalGas - parentGasTarget) @@ -82,6 +82,16 @@ func CalcBaseFee(config *params.ChainConfig, feeConfig commontype.FeeConfig, par num.Div(num, parentGasTargetBig) num.Div(num, baseFeeChangeDenominator) baseFeeDelta := math.BigMax(num, common.Big1) + + if timestamp < parent.Time { + // This should never happen as the fee window calculations should + // have already failed, but it is kept for clarity. + return nil, fmt.Errorf("cannot calculate base fee for timestamp %d prior to parent timestamp %d", + timestamp, + parent.Time, + ) + } + // If timeElapsed is greater than [params.RollupWindow], apply the // state transition to the base fee to account for the interval during // which no blocks were produced. @@ -89,17 +99,61 @@ func CalcBaseFee(config *params.ChainConfig, feeConfig commontype.FeeConfig, par // We use timeElapsed/params.RollupWindow, so that the transition is // applied for every [params.RollupWindow] seconds that has elapsed // between the parent and this block. - if timeElapsed > params.RollupWindow { - // Note: timeElapsed/params.RollupWindow must be at least 1 since - // we've checked that timeElapsed > params.RollupWindow - baseFeeDelta = new(big.Int).Mul(baseFeeDelta, new(big.Int).SetUint64(timeElapsed/params.RollupWindow)) + var ( + timeElapsed = timestamp - parent.Time + windowsElapsed = timeElapsed / params.RollupWindow + ) + if windowsElapsed > 1 { + bigWindowsElapsed := new(big.Int).SetUint64(windowsElapsed) + // Because baseFeeDelta could actually be [common.Big1], we must not + // modify the existing value of `baseFeeDelta` but instead allocate + // a new one. + baseFeeDelta = new(big.Int).Mul(baseFeeDelta, bigWindowsElapsed) } baseFee.Sub(baseFee, baseFeeDelta) } baseFee = selectBigWithinBounds(feeConfig.MinBaseFee, baseFee, MaxUint256) - return dynamicFeeWindowBytes, baseFee, nil + return baseFee, nil +} + +// calcFeeWindow takes the previous header and the timestamp of its child block +// and calculates the expected fee window. +// +// calcFeeWindow should only be called if timestamp >= config.IsSubnetEVM +func calcFeeWindow( + config *params.ChainConfig, + feeConfig commontype.FeeConfig, + parent *types.Header, + timestamp uint64, +) (DynamicFeeWindow, error) { + // If the current block is the first EIP-1559 block, or it is the genesis block + // return the initial window. + if !config.IsSubnetEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 { + return DynamicFeeWindow{}, nil + } + + dynamicFeeWindow, err := ParseDynamicFeeWindow(parent.Extra) + if err != nil { + return DynamicFeeWindow{}, err + } + + if timestamp < parent.Time { + return DynamicFeeWindow{}, fmt.Errorf("cannot calculate fee window for timestamp %d prior to parent timestamp %d", + timestamp, + parent.Time, + ) + } + timeElapsed := timestamp - parent.Time + + // Compute the new state of the gas rolling window. + dynamicFeeWindow.Add(parent.GasUsed) + + // roll the window over by the timeElapsed to generate the new rollup + // window. + dynamicFeeWindow.Shift(timeElapsed) + return dynamicFeeWindow, nil } // EstimateNextBaseFee attempts to estimate the base fee of a block built at @@ -116,8 +170,7 @@ func EstimateNextBaseFee(config *params.ChainConfig, feeConfig commontype.FeeCon } timestamp = max(timestamp, parent.Time, *config.SubnetEVMTimestamp) - _, baseFee, err := CalcBaseFee(config, feeConfig, parent, timestamp) - return baseFee, err + return CalcBaseFee(config, feeConfig, parent, timestamp) } // selectBigWithinBounds returns [value] if it is within the bounds: diff --git a/consensus/dummy/dynamic_fees_test.go b/consensus/dummy/dynamic_fees_test.go index e70dc08b5c..6af3e0d033 100644 --- a/consensus/dummy/dynamic_fees_test.go +++ b/consensus/dummy/dynamic_fees_test.go @@ -149,10 +149,11 @@ func testDynamicFeesStaysWithinRange(t *testing.T, test test) { BlockGasCostStep: big.NewInt(200_000), } - nextExtraData, nextBaseFee, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, header, block.timestamp) + nextExtraData, err := CalcExtraPrefix(params.TestChainConfig, testFeeConfig, header, block.timestamp) if err != nil { - t.Fatalf("Failed to calculate base fee at index %d: %s", index, err) + t.Fatalf("Failed to calculate extra prefix at index %d: %s", index, err) } + nextBaseFee, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, header, block.timestamp) if nextBaseFee.Cmp(test.minFee) < 0 { t.Fatalf("Expected fee to stay greater than %d, but found %d", test.minFee, nextBaseFee) } @@ -239,7 +240,7 @@ func TestCalcBaseFeeRegression(t *testing.T) { MaxBlockGasCost: big.NewInt(1_000_000), BlockGasCostStep: big.NewInt(200_000), } - _, _, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, parentHeader, timestamp) + _, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, parentHeader, timestamp) require.NoError(t, err) require.Equalf(t, 0, common.Big1.Cmp(big.NewInt(1)), "big1 should be 1, got %s", common.Big1) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 6b8467dc3c..762d6d10ca 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -388,9 +388,12 @@ func (cm *chainMaker) makeHeader(parent *types.Block, gap uint64, state *state.S if err != nil { panic(err) } - header.GasLimit = feeConfig.GasLimit.Uint64() - header.Extra, header.BaseFee, err = dummy.CalcBaseFee(cm.config, feeConfig, parent.Header(), time) + header.Extra, err = dummy.CalcExtraPrefix(cm.config, feeConfig, parent.Header(), time) + if err != nil { + panic(err) + } + header.BaseFee, err = dummy.CalcBaseFee(cm.config, feeConfig, parent.Header(), time) if err != nil { panic(err) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 2780684de3..0f5aedcd0c 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -65,11 +65,11 @@ func TestStateProcessorErrors(t *testing.T) { signer = types.LatestSigner(config) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) - var makeTx = func(key *ecdsa.PrivateKey, nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { + makeTx := func(key *ecdsa.PrivateKey, nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction { tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, key) return tx } - var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction { + mkDynamicTx := func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction { tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ Nonce: nonce, GasTipCap: gasTipCap, @@ -80,7 +80,7 @@ func TestStateProcessorErrors(t *testing.T) { }), signer, key1) return tx } - var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction { + mkDynamicCreationTx := func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction { tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ Nonce: nonce, GasTipCap: gasTipCap, @@ -91,7 +91,7 @@ func TestStateProcessorErrors(t *testing.T) { }), signer, key1) return tx } - var mkBlobTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap, blobGasFeeCap *big.Int, hashes []common.Hash) *types.Transaction { + mkBlobTx := func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap, blobGasFeeCap *big.Int, hashes []common.Hash) *types.Transaction { tx, err := types.SignTx(types.NewTx(&types.BlobTx{ Nonce: nonce, GasTipCap: uint256.MustFromBig(gasTipCap), @@ -446,7 +446,8 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr } if config.IsSubnetEVM(header.Time) { - header.Extra, header.BaseFee, _ = dummy.CalcBaseFee(config, config.FeeConfig, parent.Header(), header.Time) + header.Extra, _ = dummy.CalcExtraPrefix(config, config.FeeConfig, parent.Header(), header.Time) + header.BaseFee, _ = dummy.CalcBaseFee(config, config.FeeConfig, parent.Header(), header.Time) header.BlockGasCost = big.NewInt(0) } var receipts []*types.Receipt @@ -468,7 +469,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr } header.Root = common.BytesToHash(hasher.Sum(nil)) if config.IsCancun(header.Number, header.Time) { - var pExcess, pUsed = uint64(0), uint64(0) + pExcess, pUsed := uint64(0), uint64(0) if parent.ExcessBlobGas() != nil { pExcess = *parent.ExcessBlobGas() pUsed = *parent.BlobGasUsed() diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index b7d0c06c2b..99a6ff20f8 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -116,7 +116,7 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { BaseFee: mid, Extra: make([]byte, params.DynamicFeeExtraDataSize), } - _, baseFee, err := dummy.CalcBaseFee( + baseFee, err := dummy.CalcBaseFee( bc.config, bc.config.FeeConfig, parent, blockTime, ) if err != nil { @@ -347,7 +347,7 @@ func TestOpenDrops(t *testing.T) { storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) - os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0o700) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert a malformed transaction to verify that decoding errors (or format @@ -670,7 +670,7 @@ func TestOpenIndex(t *testing.T) { storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) - os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0o700) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert a sequence of transactions with varying price points to check that @@ -683,8 +683,8 @@ func TestOpenIndex(t *testing.T) { txExecFeeCaps = []uint64{100, 90, 200, 10, 80, 300} txBlobFeeCaps = []uint64{55, 66, 77, 33, 22, 11} - //basefeeJumps = []float64{39.098, 38.204, 44.983, 19.549, 37.204, 48.426} // log 1.125 (exec fee cap) - //blobfeeJumps = []float64{34.023, 35.570, 36.879, 29.686, 26.243, 20.358} // log 1.125 (blob fee cap) + // basefeeJumps = []float64{39.098, 38.204, 44.983, 19.549, 37.204, 48.426} // log 1.125 (exec fee cap) + // blobfeeJumps = []float64{34.023, 35.570, 36.879, 29.686, 26.243, 20.358} // log 1.125 (blob fee cap) evictExecTipCaps = []uint64{10, 10, 5, 5, 1, 1} evictExecFeeJumps = []float64{39.098, 38.204, 38.204, 19.549, 19.549, 19.549} // min(log 1.125 (exec fee cap)) @@ -759,7 +759,7 @@ func TestOpenHeap(t *testing.T) { storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) - os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0o700) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert a few transactions from a few accounts. To remove randomness from @@ -846,7 +846,7 @@ func TestOpenCap(t *testing.T) { storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) - os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0o700) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert a few transactions from a few accounts @@ -1277,7 +1277,7 @@ func TestAdd(t *testing.T) { storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) // late defer, still ok - os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0700) + os.MkdirAll(filepath.Join(storage, pendingTransactionStore), 0o700) store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil) // Insert the seed transactions for the pool startup diff --git a/miner/worker.go b/miner/worker.go index b7b5e7b9b8..2ecd85e220 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -169,7 +169,11 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte if w.chainConfig.IsSubnetEVM(timestamp) { var err error - header.Extra, header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent, timestamp) + header.Extra, err = dummy.CalcExtraPrefix(w.chainConfig, feeConfig, parent, timestamp) + if err != nil { + return nil, fmt.Errorf("failed to calculate new extra prefix: %w", err) + } + header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent, timestamp) if err != nil { return nil, fmt.Errorf("failed to calculate new base fee: %w", err) } From 098c2552077f4a779bd1b04cb92695ae09f3f52a Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Fri, 28 Feb 2025 13:42:58 +0300 Subject: [PATCH 4/6] return default fee if not subnet-evm --- core/blockchain_reader.go | 4 ++++ core/txpool/legacypool/legacypool.go | 3 +++ eth/gasprice/gasprice.go | 6 +----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index eba02b712c..d1f242d94c 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -361,11 +361,15 @@ func (bc *BlockChain) SubscribeAcceptedTransactionEvent(ch chan<- NewTxsEvent) e } // GetFeeConfigAt returns the fee configuration and the last changed block number at [parent]. +// If Subnet-EVM is not activated, returns default fee config and nil block number. // If FeeManager is activated at [parent], returns the fee config in the precompile contract state. // Otherwise returns the fee config in the chain config. // Assumes that a valid configuration is stored when the precompile is activated. func (bc *BlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) { config := bc.Config() + if !config.IsSubnetEVM(parent.Time) { + return params.DefaultFeeConfig, nil, nil + } if !config.IsPrecompileEnabled(feemanager.ContractAddress, parent.Time) { return config.FeeConfig, common.Big0, nil } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 47ddf2632b..3d8b91e62e 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1818,6 +1818,8 @@ func (pool *LegacyPool) periodicBaseFeeUpdate() { } } +// updateBaseFee updates the base fee in the tx pool based on the current head block. +// should only be called when the chain is in Subnet EVM. func (pool *LegacyPool) updateBaseFee() { pool.mu.Lock() defer pool.mu.Unlock() @@ -1829,6 +1831,7 @@ func (pool *LegacyPool) updateBaseFee() { } // assumes lock is already held +// should only be called when the chain is in Subnet EVM. func (pool *LegacyPool) updateBaseFeeAt(head *types.Header) error { feeConfig, _, err := pool.chain.GetFeeConfigAt(head) if err != nil { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 818c4623f1..8b6cc86b55 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -192,12 +192,8 @@ func NewOracle(backend OracleBackend, config Config) (*Oracle, error) { } }() feeConfig, _, err := backend.GetFeeConfigAt(backend.LastAcceptedBlock().Header()) - var minBaseFee *big.Int if err != nil { - // resort back to chain config return nil, fmt.Errorf("failed getting fee config in the oracle: %w", err) - } else { - minBaseFee = feeConfig.MinBaseFee } feeInfoProvider, err := newFeeInfoProvider(backend, minGasUsed.Uint64(), config.Blocks) if err != nil { @@ -206,7 +202,7 @@ func NewOracle(backend OracleBackend, config Config) (*Oracle, error) { return &Oracle{ backend: backend, lastPrice: minPrice, - lastBaseFee: new(big.Int).Set(minBaseFee), + lastBaseFee: new(big.Int).Set(feeConfig.MinBaseFee), minPrice: minPrice, maxPrice: maxPrice, checkBlocks: blocks, From bcec4a4bfae0a45e1bf42e2b253600ac9f507214 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Tue, 18 Feb 2025 11:59:07 -0500 Subject: [PATCH 5/6] Support all upgrades in CalcBaseFee and CalcExtraPrefix (#804) --- consensus/dummy/dynamic_fees.go | 28 +++++++++++++++++++++++----- core/chain_makers.go | 27 ++++++++++++++------------- core/state_processor_test.go | 5 ++--- miner/worker.go | 18 ++++++++---------- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/consensus/dummy/dynamic_fees.go b/consensus/dummy/dynamic_fees.go index 571409db91..88d4235876 100644 --- a/consensus/dummy/dynamic_fees.go +++ b/consensus/dummy/dynamic_fees.go @@ -23,23 +23,41 @@ var ( // CalcExtraPrefix takes the previous header and the timestamp of its child // block and calculates the expected extra prefix for the child block. -// -// CalcExtraPrefix should only be called if timestamp >= config.ApricotPhase3Timestamp func CalcExtraPrefix( config *params.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64, ) ([]byte, error) { - window, err := calcFeeWindow(config, feeConfig, parent, timestamp) - return window.Bytes(), err + switch { + case config.IsSubnetEVM(timestamp): + window, err := calcFeeWindow(config, feeConfig, parent, timestamp) + if err != nil { + return nil, fmt.Errorf("failed to calculate fee window: %w", err) + } + return window.Bytes(), nil + default: + // Prior to SubnetEVM there was no expected extra prefix. + return nil, nil + } } // CalcBaseFee takes the previous header and the timestamp of its child block // and calculates the expected base fee for the child block. // -// CalcBaseFee should only be called if timestamp >= config.IsSubnetEVMTimestamp +// Prior to SubnetEVM, the returned base fee will be nil. func CalcBaseFee(config *params.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) (*big.Int, error) { + switch { + case config.IsSubnetEVM(timestamp): + return calcBaseFeeWithWindow(config, feeConfig, parent, timestamp) + default: + // Prior to SubnetEVM the expected base fee is nil. + return nil, nil + } +} + +// calcBaseFeeWithWindow should only be called if `timestamp` >= `config.SubnetEVMTimestamp`. +func calcBaseFeeWithWindow(config *params.ChainConfig, feeConfig commontype.FeeConfig, parent *types.Header, timestamp uint64) (*big.Int, error) { // If the current block is the first EIP-1559 block, or it is the genesis block // return the initial slice and initial base fee. if !config.IsSubnetEVM(parent.Time) || parent.Number.Cmp(common.Big0) == 0 { diff --git a/core/chain_makers.go b/core/chain_makers.go index 762d6d10ca..bf0597ec9e 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -379,27 +379,28 @@ func (cm *chainMaker) makeHeader(parent *types.Block, gap uint64, state *state.S ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: engine.CalcDifficulty(cm, time, parent.Header()), - GasLimit: parent.GasLimit(), Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } + feeConfig, _, err := cm.GetFeeConfigAt(parent.Header()) + if err != nil { + panic(err) + } if cm.config.IsSubnetEVM(time) { - feeConfig, _, err := cm.GetFeeConfigAt(parent.Header()) - if err != nil { - panic(err) - } header.GasLimit = feeConfig.GasLimit.Uint64() - header.Extra, err = dummy.CalcExtraPrefix(cm.config, feeConfig, parent.Header(), time) - if err != nil { - panic(err) - } - header.BaseFee, err = dummy.CalcBaseFee(cm.config, feeConfig, parent.Header(), time) - if err != nil { - panic(err) - } } else { header.GasLimit = CalcGasLimit(parent.GasUsed(), parent.GasLimit(), parent.GasLimit(), parent.GasLimit()) } + + header.Extra, err = dummy.CalcExtraPrefix(cm.config, feeConfig, parent.Header(), time) + if err != nil { + panic(err) + } + header.BaseFee, err = dummy.CalcBaseFee(cm.config, feeConfig, parent.Header(), time) + if err != nil { + panic(err) + } + if cm.config.IsCancun(header.Number, header.Time) { var ( parentExcessBlobGas uint64 diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 0f5aedcd0c..6360d6b7ca 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -444,10 +444,9 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr Time: parent.Time() + 10, UncleHash: types.EmptyUncleHash, } - + header.Extra, _ = dummy.CalcExtraPrefix(config, config.FeeConfig, parent.Header(), header.Time) + header.BaseFee, _ = dummy.CalcBaseFee(config, config.FeeConfig, parent.Header(), header.Time) if config.IsSubnetEVM(header.Time) { - header.Extra, _ = dummy.CalcExtraPrefix(config, config.FeeConfig, parent.Header(), header.Time) - header.BaseFee, _ = dummy.CalcBaseFee(config, config.FeeConfig, parent.Header(), header.Time) header.BlockGasCost = big.NewInt(0) } var receipts []*types.Receipt diff --git a/miner/worker.go b/miner/worker.go index 2ecd85e220..cdf49eb7e1 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -167,17 +167,15 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte Time: timestamp, } - if w.chainConfig.IsSubnetEVM(timestamp) { - var err error - header.Extra, err = dummy.CalcExtraPrefix(w.chainConfig, feeConfig, parent, timestamp) - if err != nil { - return nil, fmt.Errorf("failed to calculate new extra prefix: %w", err) - } - header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent, timestamp) - if err != nil { - return nil, fmt.Errorf("failed to calculate new base fee: %w", err) - } + header.Extra, err = dummy.CalcExtraPrefix(w.chainConfig, feeConfig, parent, timestamp) + if err != nil { + return nil, fmt.Errorf("failed to calculate new extra prefix: %w", err) + } + header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, feeConfig, parent, timestamp) + if err != nil { + return nil, fmt.Errorf("failed to calculate new base fee: %w", err) } + // Apply EIP-4844, EIP-4788. if w.chainConfig.IsCancun(header.Number, header.Time) { var excessBlobGas uint64 From 615ba2addcab3d59a0e7fa6e2aa6b9154b4f75e1 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Fri, 28 Feb 2025 21:35:18 +0300 Subject: [PATCH 6/6] add err check to test --- consensus/dummy/dynamic_fees_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/consensus/dummy/dynamic_fees_test.go b/consensus/dummy/dynamic_fees_test.go index 6af3e0d033..562365b480 100644 --- a/consensus/dummy/dynamic_fees_test.go +++ b/consensus/dummy/dynamic_fees_test.go @@ -154,6 +154,9 @@ func testDynamicFeesStaysWithinRange(t *testing.T, test test) { t.Fatalf("Failed to calculate extra prefix at index %d: %s", index, err) } nextBaseFee, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, header, block.timestamp) + if err != nil { + t.Fatalf("Failed to calculate base fee at index %d: %s", index, err) + } if nextBaseFee.Cmp(test.minFee) < 0 { t.Fatalf("Expected fee to stay greater than %d, but found %d", test.minFee, nextBaseFee) }