github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/gasprice/gasprice_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2020 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package gasprice 28 29 import ( 30 "context" 31 "math/big" 32 "testing" 33 "time" 34 35 "github.com/MetalBlockchain/subnet-evm/commontype" 36 "github.com/MetalBlockchain/subnet-evm/consensus/dummy" 37 "github.com/MetalBlockchain/subnet-evm/core" 38 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 39 "github.com/MetalBlockchain/subnet-evm/core/types" 40 "github.com/MetalBlockchain/subnet-evm/core/vm" 41 "github.com/MetalBlockchain/subnet-evm/ethdb" 42 "github.com/MetalBlockchain/subnet-evm/params" 43 "github.com/MetalBlockchain/subnet-evm/precompile" 44 "github.com/MetalBlockchain/subnet-evm/rpc" 45 "github.com/ethereum/go-ethereum/common" 46 "github.com/ethereum/go-ethereum/crypto" 47 "github.com/ethereum/go-ethereum/event" 48 "github.com/stretchr/testify/require" 49 ) 50 51 var ( 52 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 53 addr = crypto.PubkeyToAddress(key.PublicKey) 54 bal, _ = new(big.Int).SetString("100000000000000000000000", 10) 55 ) 56 57 type testBackend struct { 58 db ethdb.Database 59 chain *core.BlockChain 60 acceptedEvent chan<- core.ChainEvent 61 } 62 63 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 64 if number == rpc.LatestBlockNumber { 65 return b.chain.CurrentBlock().Header(), nil 66 } 67 return b.chain.GetHeaderByNumber(uint64(number)), nil 68 } 69 70 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 71 if number == rpc.LatestBlockNumber { 72 return b.chain.CurrentBlock(), nil 73 } 74 return b.chain.GetBlockByNumber(uint64(number)), nil 75 } 76 77 func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 78 return b.chain.GetReceiptsByHash(hash), nil 79 } 80 81 func (b *testBackend) ChainConfig() *params.ChainConfig { 82 return b.chain.Config() 83 } 84 85 func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 86 return nil 87 } 88 89 func (b *testBackend) SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription { 90 b.acceptedEvent = ch 91 return nil 92 } 93 94 func (b *testBackend) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) { 95 return b.chain.GetFeeConfigAt(parent) 96 } 97 98 func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend { 99 var gspec = &core.Genesis{ 100 Config: config, 101 Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, 102 } 103 104 engine := dummy.NewETHFaker() 105 db := rawdb.NewMemoryDatabase() 106 genesis := gspec.MustCommit(db) 107 108 // Generate testing blocks 109 blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db, numBlocks, 0, genBlocks) 110 if err != nil { 111 t.Fatal(err) 112 } 113 // Construct testing chain 114 diskdb := rawdb.NewMemoryDatabase() 115 gspec.Commit(diskdb) 116 chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec.Config, engine, vm.Config{}, common.Hash{}) 117 if err != nil { 118 t.Fatalf("Failed to create local chain, %v", err) 119 } 120 if _, err := chain.InsertChain(blocks); err != nil { 121 t.Fatalf("Failed to insert chain, %v", err) 122 } 123 return &testBackend{chain: chain} 124 } 125 126 func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend { 127 var gspec = &core.Genesis{ 128 Config: config, 129 Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, 130 } 131 132 engine := dummy.NewFaker() 133 db := rawdb.NewMemoryDatabase() 134 genesis := gspec.MustCommit(db) 135 136 // Generate testing blocks 137 blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db, numBlocks, 1, genBlocks) 138 if err != nil { 139 t.Fatal(err) 140 } 141 // Construct testing chain 142 diskdb := rawdb.NewMemoryDatabase() 143 gspec.Commit(diskdb) 144 chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec.Config, engine, vm.Config{}, common.Hash{}) 145 if err != nil { 146 t.Fatalf("Failed to create local chain, %v", err) 147 } 148 if _, err := chain.InsertChain(blocks); err != nil { 149 t.Fatalf("Failed to insert chain, %v", err) 150 } 151 return &testBackend{chain: chain, db: db} 152 } 153 154 func (b *testBackend) MinRequiredTip(ctx context.Context, header *types.Header) (*big.Int, error) { 155 return dummy.MinRequiredTip(b.chain.Config(), header) 156 } 157 158 func (b *testBackend) CurrentHeader() *types.Header { 159 return b.chain.CurrentHeader() 160 } 161 162 func (b *testBackend) LastAcceptedBlock() *types.Block { 163 return b.chain.CurrentBlock() 164 } 165 166 func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { 167 return b.chain.GetBlockByNumber(number) 168 } 169 170 type suggestTipCapTest struct { 171 chainConfig *params.ChainConfig 172 numBlocks int 173 genBlock func(i int, b *core.BlockGen) 174 expectedTip *big.Int 175 } 176 177 func defaultOracleConfig() Config { 178 return Config{ 179 Blocks: 20, 180 Percentile: 60, 181 MaxLookbackSeconds: 80, 182 } 183 } 184 185 // timeCrunchOracleConfig returns a config with [MaxLookbackSeconds] set to 5 186 // to ensure that during gas price estimation, we will hit the time based look back limit 187 func timeCrunchOracleConfig() Config { 188 return Config{ 189 Blocks: 20, 190 Percentile: 60, 191 MaxLookbackSeconds: 5, 192 } 193 } 194 195 func applyGasPriceTest(t *testing.T, test suggestTipCapTest, config Config) { 196 if test.genBlock == nil { 197 test.genBlock = func(i int, b *core.BlockGen) {} 198 } 199 backend := newTestBackend(t, test.chainConfig, test.numBlocks, test.genBlock) 200 oracle, err := NewOracle(backend, config) 201 require.NoError(t, err) 202 203 // mock time to be consistent across different CI runs 204 // sets currentTime to be 20 seconds 205 oracle.clock.Set(time.Unix(20, 0)) 206 207 got, err := oracle.SuggestTipCap(context.Background()) 208 require.NoError(t, err) 209 210 if got.Cmp(test.expectedTip) != 0 { 211 t.Fatalf("Expected tip (%d), got tip (%d)", test.expectedTip, got) 212 } 213 } 214 215 func testGenBlock(t *testing.T, tip int64, numTx int) func(int, *core.BlockGen) { 216 return func(i int, b *core.BlockGen) { 217 b.SetCoinbase(common.Address{1}) 218 219 txTip := big.NewInt(tip * params.GWei) 220 signer := types.LatestSigner(params.TestChainConfig) 221 baseFee := b.BaseFee() 222 feeCap := new(big.Int).Add(baseFee, txTip) 223 for j := 0; j < numTx; j++ { 224 tx := types.NewTx(&types.DynamicFeeTx{ 225 ChainID: params.TestChainConfig.ChainID, 226 Nonce: b.TxNonce(addr), 227 To: &common.Address{}, 228 Gas: params.TxGas, 229 GasFeeCap: feeCap, 230 GasTipCap: txTip, 231 Data: []byte{}, 232 }) 233 tx, err := types.SignTx(tx, signer, key) 234 require.NoError(t, err, "failed to create tx") 235 b.AddTx(tx) 236 } 237 } 238 } 239 240 func TestSuggestTipCapNetworkUpgrades(t *testing.T) { 241 tests := map[string]suggestTipCapTest{ 242 "subnet evm": { 243 chainConfig: params.TestChainConfig, 244 expectedTip: DefaultMinPrice, 245 }, 246 } 247 248 for name, test := range tests { 249 t.Run(name, func(t *testing.T) { 250 applyGasPriceTest(t, test, defaultOracleConfig()) 251 }) 252 } 253 } 254 255 func TestSuggestTipCap(t *testing.T) { 256 applyGasPriceTest(t, suggestTipCapTest{ 257 chainConfig: params.TestChainConfig, 258 numBlocks: 3, 259 genBlock: testGenBlock(t, 55, 370), 260 expectedTip: big.NewInt(643_500_643), 261 }, defaultOracleConfig()) 262 } 263 264 func TestSuggestTipCapSimpleFloor(t *testing.T) { 265 applyGasPriceTest(t, suggestTipCapTest{ 266 chainConfig: params.TestChainConfig, 267 numBlocks: 1, 268 genBlock: testGenBlock(t, 55, 370), 269 expectedTip: common.Big0, 270 }, defaultOracleConfig()) 271 } 272 273 func TestSuggestTipCapSmallTips(t *testing.T) { 274 tip := big.NewInt(550 * params.GWei) 275 applyGasPriceTest(t, suggestTipCapTest{ 276 chainConfig: params.TestChainConfig, 277 numBlocks: 3, 278 genBlock: func(i int, b *core.BlockGen) { 279 b.SetCoinbase(common.Address{1}) 280 281 signer := types.LatestSigner(params.TestChainConfig) 282 baseFee := b.BaseFee() 283 feeCap := new(big.Int).Add(baseFee, tip) 284 for j := 0; j < 185; j++ { 285 tx := types.NewTx(&types.DynamicFeeTx{ 286 ChainID: params.TestChainConfig.ChainID, 287 Nonce: b.TxNonce(addr), 288 To: &common.Address{}, 289 Gas: params.TxGas, 290 GasFeeCap: feeCap, 291 GasTipCap: tip, 292 Data: []byte{}, 293 }) 294 tx, err := types.SignTx(tx, signer, key) 295 if err != nil { 296 t.Fatalf("failed to create tx: %s", err) 297 } 298 b.AddTx(tx) 299 tx = types.NewTx(&types.DynamicFeeTx{ 300 ChainID: params.TestChainConfig.ChainID, 301 Nonce: b.TxNonce(addr), 302 To: &common.Address{}, 303 Gas: params.TxGas, 304 GasFeeCap: feeCap, 305 GasTipCap: common.Big1, 306 Data: []byte{}, 307 }) 308 tx, err = types.SignTx(tx, signer, key) 309 if err != nil { 310 t.Fatalf("failed to create tx: %s", err) 311 } 312 b.AddTx(tx) 313 } 314 }, 315 expectedTip: big.NewInt(643_500_643), 316 }, defaultOracleConfig()) 317 } 318 319 func TestSuggestTipCapMinGas(t *testing.T) { 320 applyGasPriceTest(t, suggestTipCapTest{ 321 chainConfig: params.TestChainConfig, 322 numBlocks: 3, 323 genBlock: testGenBlock(t, 500, 50), 324 expectedTip: big.NewInt(0), 325 }, defaultOracleConfig()) 326 } 327 328 // Regression test to ensure that SuggestPrice does not panic prior to activation of Subnet EVM 329 // Note: support for gas estimation without activated hard forks has been deprecated, but we still 330 // ensure that the call does not panic. 331 func TestSuggestGasPricePreSubnetEVM(t *testing.T) { 332 config := Config{ 333 Blocks: 20, 334 Percentile: 60, 335 } 336 337 backend := newTestBackend(t, params.TestPreSubnetEVMConfig, 3, func(i int, b *core.BlockGen) { 338 b.SetCoinbase(common.Address{1}) 339 340 signer := types.LatestSigner(params.TestPreSubnetEVMConfig) 341 gasPrice := big.NewInt(params.MinGasPrice) 342 for j := 0; j < 50; j++ { 343 tx := types.NewTx(&types.LegacyTx{ 344 Nonce: b.TxNonce(addr), 345 To: &common.Address{}, 346 Gas: params.TxGas, 347 GasPrice: gasPrice, 348 Data: []byte{}, 349 }) 350 tx, err := types.SignTx(tx, signer, key) 351 if err != nil { 352 t.Fatalf("failed to create tx: %s", err) 353 } 354 b.AddTx(tx) 355 } 356 }) 357 oracle, err := NewOracle(backend, config) 358 if err != nil { 359 t.Fatal(err) 360 } 361 362 _, err = oracle.SuggestPrice(context.Background()) 363 if err != nil { 364 t.Fatal(err) 365 } 366 } 367 368 // Regression test to ensure that SuggestPrice does not panic prior to activation of SubnetEVM 369 // Note: support for gas estimation without activated hard forks has been deprecated, but we still 370 // ensure that the call does not panic. 371 func TestSuggestGasPricePreAP3(t *testing.T) { 372 config := Config{ 373 Blocks: 20, 374 Percentile: 60, 375 } 376 377 backend := newTestBackend(t, params.TestChainConfig, 3, func(i int, b *core.BlockGen) { 378 b.SetCoinbase(common.Address{1}) 379 380 signer := types.LatestSigner(params.TestChainConfig) 381 gasPrice := big.NewInt(params.MinGasPrice) 382 for j := 0; j < 50; j++ { 383 tx := types.NewTx(&types.LegacyTx{ 384 Nonce: b.TxNonce(addr), 385 To: &common.Address{}, 386 Gas: params.TxGas, 387 GasPrice: gasPrice, 388 Data: []byte{}, 389 }) 390 tx, err := types.SignTx(tx, signer, key) 391 if err != nil { 392 t.Fatalf("failed to create tx: %s", err) 393 } 394 b.AddTx(tx) 395 } 396 }) 397 oracle, err := NewOracle(backend, config) 398 if err != nil { 399 t.Fatal(err) 400 } 401 402 _, err = oracle.SuggestPrice(context.Background()) 403 if err != nil { 404 t.Fatal(err) 405 } 406 } 407 408 func TestSuggestTipCapMaxBlocksLookback(t *testing.T) { 409 applyGasPriceTest(t, suggestTipCapTest{ 410 chainConfig: params.TestChainConfig, 411 numBlocks: 20, 412 genBlock: testGenBlock(t, 550, 370), 413 expectedTip: big.NewInt(5_807_226_110), 414 }, defaultOracleConfig()) 415 } 416 417 func TestSuggestTipCapMaxBlocksSecondsLookback(t *testing.T) { 418 applyGasPriceTest(t, suggestTipCapTest{ 419 chainConfig: params.TestChainConfig, 420 numBlocks: 20, 421 genBlock: testGenBlock(t, 550, 370), 422 expectedTip: big.NewInt(10_384_877_851), 423 }, timeCrunchOracleConfig()) 424 } 425 426 // Regression test to ensure the last estimation of base fee is not used 427 // for the block immediately following a fee configuration update. 428 func TestSuggestGasPriceAfterFeeConfigUpdate(t *testing.T) { 429 require := require.New(t) 430 config := Config{ 431 Blocks: 20, 432 Percentile: 60, 433 } 434 435 // create a chain config with fee manager enabled at genesis with [addr] as the admin 436 chainConfig := *params.TestChainConfig 437 chainConfig.FeeManagerConfig = precompile.NewFeeManagerConfig(big.NewInt(0), []common.Address{addr}, nil, nil) 438 439 // create a fee config with higher MinBaseFee and prepare it for inclusion in a tx 440 signer := types.LatestSigner(params.TestChainConfig) 441 highFeeConfig := chainConfig.FeeConfig 442 highFeeConfig.MinBaseFee = big.NewInt(28_000_000_000) 443 data, err := precompile.PackSetFeeConfig(highFeeConfig) 444 require.NoError(err) 445 446 // before issuing the block changing the fee into the chain, the fee estimation should 447 // follow the fee config in genesis. 448 backend := newTestBackend(t, &chainConfig, 0, func(i int, b *core.BlockGen) {}) 449 oracle, err := NewOracle(backend, config) 450 require.NoError(err) 451 got, err := oracle.SuggestPrice(context.Background()) 452 require.NoError(err) 453 require.Equal(chainConfig.FeeConfig.MinBaseFee, got) 454 455 // issue the block with tx that changes the fee 456 genesis := backend.chain.Genesis() 457 engine := backend.chain.Engine() 458 blocks, _, err := core.GenerateChain(&chainConfig, genesis, engine, backend.db, 1, 0, func(i int, b *core.BlockGen) { 459 b.SetCoinbase(common.Address{1}) 460 461 // admin issues tx to change fee config to higher MinBaseFee 462 tx := types.NewTx(&types.DynamicFeeTx{ 463 ChainID: chainConfig.ChainID, 464 Nonce: b.TxNonce(addr), 465 To: &precompile.FeeConfigManagerAddress, 466 Gas: chainConfig.FeeConfig.GasLimit.Uint64(), 467 Value: common.Big0, 468 GasFeeCap: chainConfig.FeeConfig.MinBaseFee, // give low fee, it should work since we still haven't applied high fees 469 GasTipCap: common.Big0, 470 Data: data, 471 }) 472 tx, err = types.SignTx(tx, signer, key) 473 require.NoError(err, "failed to create tx") 474 b.AddTx(tx) 475 }) 476 require.NoError(err) 477 _, err = backend.chain.InsertChain(blocks) 478 require.NoError(err) 479 480 // verify the suggested price follows the new fee config. 481 got, err = oracle.SuggestPrice(context.Background()) 482 require.NoError(err) 483 require.Equal(highFeeConfig.MinBaseFee, got) 484 }