github.com/dim4egster/coreth@v0.10.2/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/dim4egster/coreth/consensus/dummy" 36 "github.com/dim4egster/coreth/core" 37 "github.com/dim4egster/coreth/core/rawdb" 38 "github.com/dim4egster/coreth/core/state" 39 "github.com/dim4egster/coreth/core/types" 40 "github.com/dim4egster/coreth/core/vm" 41 "github.com/dim4egster/coreth/params" 42 "github.com/dim4egster/coreth/rpc" 43 "github.com/ethereum/go-ethereum/common" 44 "github.com/ethereum/go-ethereum/crypto" 45 "github.com/ethereum/go-ethereum/event" 46 ) 47 48 const testHead = 32 49 50 var ( 51 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 52 addr = crypto.PubkeyToAddress(key.PublicKey) 53 bal, _ = new(big.Int).SetString("100000000000000000000000", 10) 54 ) 55 56 type testBackend struct { 57 chain *core.BlockChain 58 pending bool // pending block available 59 } 60 61 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 62 if number == rpc.LatestBlockNumber { 63 return b.chain.CurrentBlock().Header(), nil 64 } 65 return b.chain.GetHeaderByNumber(uint64(number)), nil 66 } 67 68 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 69 if number == rpc.LatestBlockNumber { 70 return b.chain.CurrentBlock(), nil 71 } 72 return b.chain.GetBlockByNumber(uint64(number)), nil 73 } 74 75 func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 76 return b.chain.GetReceiptsByHash(hash), nil 77 } 78 79 func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { 80 if b.pending { 81 block := b.chain.GetBlockByNumber(testHead + 1) 82 return block, b.chain.GetReceiptsByHash(block.Hash()) 83 } 84 return nil, nil 85 } 86 87 func (b *testBackend) ChainConfig() *params.ChainConfig { 88 return b.chain.Config() 89 } 90 91 func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 92 return nil 93 } 94 95 func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.Int, genBlocks func(i int, b *core.BlockGen)) *testBackend { 96 var gspec = &core.Genesis{ 97 Config: config, 98 Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, 99 } 100 101 engine := dummy.NewETHFaker() 102 db := rawdb.NewMemoryDatabase() 103 genesis := gspec.MustCommit(db) 104 105 // Generate testing blocks 106 blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db, numBlocks, 0, genBlocks) 107 if err != nil { 108 t.Fatal(err) 109 } 110 // Construct testing chain 111 diskdb := rawdb.NewMemoryDatabase() 112 gspec.Commit(diskdb) 113 chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec.Config, engine, vm.Config{}, common.Hash{}) 114 if err != nil { 115 t.Fatalf("Failed to create local chain, %v", err) 116 } 117 if _, err := chain.InsertChain(blocks); err != nil { 118 t.Fatalf("Failed to insert chain, %v", err) 119 } 120 return &testBackend{chain: chain} 121 } 122 123 func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.Int, genBlocks func(i int, b *core.BlockGen)) *testBackend { 124 var gspec = &core.Genesis{ 125 Config: config, 126 Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}}, 127 } 128 129 engine := dummy.NewDummyEngine(&dummy.ConsensusCallbacks{ 130 OnFinalizeAndAssemble: func(header *types.Header, state *state.StateDB, txs []*types.Transaction) ([]byte, *big.Int, *big.Int, error) { 131 return nil, common.Big0, extDataGasUsage, nil 132 }, 133 OnExtraStateChange: func(block *types.Block, state *state.StateDB) (*big.Int, *big.Int, error) { 134 return common.Big0, extDataGasUsage, nil 135 }, 136 }) 137 db := rawdb.NewMemoryDatabase() 138 genesis := gspec.MustCommit(db) 139 140 // Generate testing blocks 141 142 blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db, numBlocks, 1, genBlocks) 143 if err != nil { 144 t.Fatal(err) 145 } 146 // Construct testing chain 147 diskdb := rawdb.NewMemoryDatabase() 148 gspec.Commit(diskdb) 149 chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec.Config, engine, vm.Config{}, common.Hash{}) 150 if err != nil { 151 t.Fatalf("Failed to create local chain, %v", err) 152 } 153 if _, err := chain.InsertChain(blocks); err != nil { 154 t.Fatalf("Failed to insert chain, %v", err) 155 } 156 return &testBackend{chain: chain} 157 } 158 159 func (b *testBackend) MinRequiredTip(ctx context.Context, header *types.Header) (*big.Int, error) { 160 return dummy.MinRequiredTip(b.chain.Config(), header) 161 } 162 163 func (b *testBackend) CurrentHeader() *types.Header { 164 return b.chain.CurrentHeader() 165 } 166 167 func (b *testBackend) LastAcceptedBlock() *types.Block { 168 return b.chain.CurrentBlock() 169 } 170 171 func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { 172 return b.chain.GetBlockByNumber(number) 173 } 174 175 type suggestTipCapTest struct { 176 chainConfig *params.ChainConfig 177 numBlocks int 178 extDataGasUsage *big.Int 179 genBlock func(i int, b *core.BlockGen) 180 expectedTip *big.Int 181 } 182 183 func defaultOracleConfig() Config { 184 return Config{ 185 Blocks: 20, 186 Percentile: 60, 187 MaxLookbackSeconds: 80, 188 } 189 } 190 191 // timeCrunchOracleConfig returns a config with [MaxLookbackSeconds] set to 5 192 // to ensure that during gas price estimation, we will hit the time based look back limit 193 func timeCrunchOracleConfig() Config { 194 return Config{ 195 Blocks: 20, 196 Percentile: 60, 197 MaxLookbackSeconds: 5, 198 } 199 } 200 201 func applyGasPriceTest(t *testing.T, test suggestTipCapTest, config Config) { 202 if test.genBlock == nil { 203 test.genBlock = func(i int, b *core.BlockGen) {} 204 } 205 backend := newTestBackend(t, test.chainConfig, test.numBlocks, test.extDataGasUsage, test.genBlock) 206 oracle := NewOracle(backend, config) 207 208 // mock time to be consistent across different CI runs 209 // sets currentTime to be 20 seconds 210 oracle.clock.Set(time.Unix(20, 0)) 211 212 got, err := oracle.SuggestTipCap(context.Background()) 213 if err != nil { 214 t.Fatal(err) 215 } 216 if got.Cmp(test.expectedTip) != 0 { 217 t.Fatalf("Expected tip (%d), got tip (%d)", test.expectedTip, got) 218 } 219 } 220 221 func TestSuggestTipCapEmptyExtDataGasUsage(t *testing.T) { 222 txTip := big.NewInt(55 * params.GWei) 223 applyGasPriceTest(t, suggestTipCapTest{ 224 chainConfig: params.TestChainConfig, 225 numBlocks: 3, 226 extDataGasUsage: nil, 227 genBlock: func(i int, b *core.BlockGen) { 228 b.SetCoinbase(common.Address{1}) 229 230 signer := types.LatestSigner(params.TestChainConfig) 231 baseFee := b.BaseFee() 232 feeCap := new(big.Int).Add(baseFee, txTip) 233 for j := 0; j < 370; j++ { 234 tx := types.NewTx(&types.DynamicFeeTx{ 235 ChainID: params.TestChainConfig.ChainID, 236 Nonce: b.TxNonce(addr), 237 To: &common.Address{}, 238 Gas: params.TxGas, 239 GasFeeCap: feeCap, 240 GasTipCap: txTip, 241 Data: []byte{}, 242 }) 243 tx, err := types.SignTx(tx, signer, key) 244 if err != nil { 245 t.Fatalf("failed to create tx: %s", err) 246 } 247 b.AddTx(tx) 248 } 249 }, 250 expectedTip: big.NewInt(5_713_963_963), 251 }, defaultOracleConfig()) 252 } 253 254 func TestSuggestTipCapSimple(t *testing.T) { 255 txTip := big.NewInt(55 * params.GWei) 256 applyGasPriceTest(t, suggestTipCapTest{ 257 chainConfig: params.TestChainConfig, 258 numBlocks: 3, 259 extDataGasUsage: common.Big0, 260 genBlock: func(i int, b *core.BlockGen) { 261 b.SetCoinbase(common.Address{1}) 262 263 signer := types.LatestSigner(params.TestChainConfig) 264 baseFee := b.BaseFee() 265 feeCap := new(big.Int).Add(baseFee, txTip) 266 for j := 0; j < 370; j++ { 267 tx := types.NewTx(&types.DynamicFeeTx{ 268 ChainID: params.TestChainConfig.ChainID, 269 Nonce: b.TxNonce(addr), 270 To: &common.Address{}, 271 Gas: params.TxGas, 272 GasFeeCap: feeCap, 273 GasTipCap: txTip, 274 Data: []byte{}, 275 }) 276 tx, err := types.SignTx(tx, signer, key) 277 if err != nil { 278 t.Fatalf("failed to create tx: %s", err) 279 } 280 b.AddTx(tx) 281 } 282 }, 283 expectedTip: big.NewInt(5_713_963_963), 284 }, defaultOracleConfig()) 285 } 286 287 func TestSuggestTipCapSimpleFloor(t *testing.T) { 288 txTip := big.NewInt(55 * params.GWei) 289 applyGasPriceTest(t, suggestTipCapTest{ 290 chainConfig: params.TestChainConfig, 291 numBlocks: 1, 292 extDataGasUsage: common.Big0, 293 genBlock: func(i int, b *core.BlockGen) { 294 b.SetCoinbase(common.Address{1}) 295 296 signer := types.LatestSigner(params.TestChainConfig) 297 baseFee := b.BaseFee() 298 feeCap := new(big.Int).Add(baseFee, txTip) 299 for j := 0; j < 370; j++ { 300 tx := types.NewTx(&types.DynamicFeeTx{ 301 ChainID: params.TestChainConfig.ChainID, 302 Nonce: b.TxNonce(addr), 303 To: &common.Address{}, 304 Gas: params.TxGas, 305 GasFeeCap: feeCap, 306 GasTipCap: txTip, 307 Data: []byte{}, 308 }) 309 tx, err := types.SignTx(tx, signer, key) 310 if err != nil { 311 t.Fatalf("failed to create tx: %s", err) 312 } 313 b.AddTx(tx) 314 } 315 }, 316 expectedTip: common.Big0, 317 }, defaultOracleConfig()) 318 } 319 320 func TestSuggestTipCapSmallTips(t *testing.T) { 321 tip := big.NewInt(550 * params.GWei) 322 applyGasPriceTest(t, suggestTipCapTest{ 323 chainConfig: params.TestChainConfig, 324 numBlocks: 3, 325 extDataGasUsage: common.Big0, 326 genBlock: func(i int, b *core.BlockGen) { 327 b.SetCoinbase(common.Address{1}) 328 329 signer := types.LatestSigner(params.TestChainConfig) 330 baseFee := b.BaseFee() 331 feeCap := new(big.Int).Add(baseFee, tip) 332 for j := 0; j < 185; j++ { 333 tx := types.NewTx(&types.DynamicFeeTx{ 334 ChainID: params.TestChainConfig.ChainID, 335 Nonce: b.TxNonce(addr), 336 To: &common.Address{}, 337 Gas: params.TxGas, 338 GasFeeCap: feeCap, 339 GasTipCap: tip, 340 Data: []byte{}, 341 }) 342 tx, err := types.SignTx(tx, signer, key) 343 if err != nil { 344 t.Fatalf("failed to create tx: %s", err) 345 } 346 b.AddTx(tx) 347 tx = types.NewTx(&types.DynamicFeeTx{ 348 ChainID: params.TestChainConfig.ChainID, 349 Nonce: b.TxNonce(addr), 350 To: &common.Address{}, 351 Gas: params.TxGas, 352 GasFeeCap: feeCap, 353 GasTipCap: common.Big1, 354 Data: []byte{}, 355 }) 356 tx, err = types.SignTx(tx, signer, key) 357 if err != nil { 358 t.Fatalf("failed to create tx: %s", err) 359 } 360 b.AddTx(tx) 361 } 362 }, 363 // NOTE: small tips do not bias estimate 364 expectedTip: big.NewInt(5_713_963_963), 365 }, defaultOracleConfig()) 366 } 367 368 func TestSuggestTipCapExtDataUsage(t *testing.T) { 369 txTip := big.NewInt(55 * params.GWei) 370 applyGasPriceTest(t, suggestTipCapTest{ 371 chainConfig: params.TestChainConfig, 372 numBlocks: 3, 373 extDataGasUsage: big.NewInt(10_000), 374 genBlock: func(i int, b *core.BlockGen) { 375 b.SetCoinbase(common.Address{1}) 376 377 signer := types.LatestSigner(params.TestChainConfig) 378 baseFee := b.BaseFee() 379 feeCap := new(big.Int).Add(baseFee, txTip) 380 for j := 0; j < 370; j++ { 381 tx := types.NewTx(&types.DynamicFeeTx{ 382 ChainID: params.TestChainConfig.ChainID, 383 Nonce: b.TxNonce(addr), 384 To: &common.Address{}, 385 Gas: params.TxGas, 386 GasFeeCap: feeCap, 387 GasTipCap: txTip, 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 expectedTip: big.NewInt(5_706_726_649), 398 }, defaultOracleConfig()) 399 } 400 401 func TestSuggestTipCapMinGas(t *testing.T) { 402 txTip := big.NewInt(500 * params.GWei) 403 applyGasPriceTest(t, suggestTipCapTest{ 404 chainConfig: params.TestChainConfig, 405 numBlocks: 3, 406 extDataGasUsage: common.Big0, 407 genBlock: func(i int, b *core.BlockGen) { 408 b.SetCoinbase(common.Address{1}) 409 410 signer := types.LatestSigner(params.TestChainConfig) 411 baseFee := b.BaseFee() 412 feeCap := new(big.Int).Add(baseFee, txTip) 413 for j := 0; j < 50; j++ { 414 tx := types.NewTx(&types.DynamicFeeTx{ 415 ChainID: params.TestChainConfig.ChainID, 416 Nonce: b.TxNonce(addr), 417 To: &common.Address{}, 418 Gas: params.TxGas, 419 GasFeeCap: feeCap, 420 GasTipCap: txTip, 421 Data: []byte{}, 422 }) 423 tx, err := types.SignTx(tx, signer, key) 424 if err != nil { 425 t.Fatalf("failed to create tx: %s", err) 426 } 427 b.AddTx(tx) 428 } 429 }, 430 expectedTip: big.NewInt(0), 431 }, defaultOracleConfig()) 432 } 433 434 // Regression test to ensure that SuggestPrice does not panic prior to activation of ApricotPhase3 435 // Note: support for gas estimation without activated hard forks has been deprecated, but we still 436 // ensure that the call does not panic. 437 func TestSuggestGasPricePreAP3(t *testing.T) { 438 config := Config{ 439 Blocks: 20, 440 Percentile: 60, 441 } 442 443 backend := newTestBackend(t, params.TestApricotPhase2Config, 3, nil, func(i int, b *core.BlockGen) { 444 b.SetCoinbase(common.Address{1}) 445 446 signer := types.LatestSigner(params.TestApricotPhase2Config) 447 gasPrice := big.NewInt(params.ApricotPhase1MinGasPrice) 448 for j := 0; j < 50; j++ { 449 tx := types.NewTx(&types.LegacyTx{ 450 Nonce: b.TxNonce(addr), 451 To: &common.Address{}, 452 Gas: params.TxGas, 453 GasPrice: gasPrice, 454 Data: []byte{}, 455 }) 456 tx, err := types.SignTx(tx, signer, key) 457 if err != nil { 458 t.Fatalf("failed to create tx: %s", err) 459 } 460 b.AddTx(tx) 461 } 462 }) 463 oracle := NewOracle(backend, config) 464 465 _, err := oracle.SuggestPrice(context.Background()) 466 if err != nil { 467 t.Fatal(err) 468 } 469 } 470 471 func TestSuggestTipCapMaxBlocksLookback(t *testing.T) { 472 txTip := big.NewInt(550 * params.GWei) 473 474 applyGasPriceTest(t, suggestTipCapTest{ 475 chainConfig: params.TestChainConfig, 476 numBlocks: 20, 477 extDataGasUsage: common.Big0, 478 genBlock: func(i int, b *core.BlockGen) { 479 b.SetCoinbase(common.Address{1}) 480 481 signer := types.LatestSigner(params.TestChainConfig) 482 baseFee := b.BaseFee() 483 feeCap := new(big.Int).Add(baseFee, txTip) 484 for j := 0; j < 370; j++ { 485 tx := types.NewTx(&types.DynamicFeeTx{ 486 ChainID: params.TestChainConfig.ChainID, 487 Nonce: b.TxNonce(addr), 488 To: &common.Address{}, 489 Gas: params.TxGas, 490 GasFeeCap: feeCap, 491 GasTipCap: txTip, 492 Data: []byte{}, 493 }) 494 tx, err := types.SignTx(tx, signer, key) 495 if err != nil { 496 t.Fatalf("failed to create tx: %s", err) 497 } 498 b.AddTx(tx) 499 } 500 }, 501 expectedTip: big.NewInt(51_565_264_256), 502 }, defaultOracleConfig()) 503 } 504 505 func TestSuggestTipCapMaxBlocksSecondsLookback(t *testing.T) { 506 txTip := big.NewInt(550 * params.GWei) 507 applyGasPriceTest(t, suggestTipCapTest{ 508 chainConfig: params.TestChainConfig, 509 numBlocks: 20, 510 extDataGasUsage: big.NewInt(1), 511 genBlock: func(i int, b *core.BlockGen) { 512 b.SetCoinbase(common.Address{1}) 513 514 signer := types.LatestSigner(params.TestChainConfig) 515 baseFee := b.BaseFee() 516 feeCap := new(big.Int).Add(baseFee, txTip) 517 for j := 0; j < 370; j++ { 518 tx := types.NewTx(&types.DynamicFeeTx{ 519 ChainID: params.TestChainConfig.ChainID, 520 Nonce: b.TxNonce(addr), 521 To: &common.Address{}, 522 Gas: params.TxGas, 523 GasFeeCap: feeCap, 524 GasTipCap: txTip, 525 Data: []byte{}, 526 }) 527 tx, err := types.SignTx(tx, signer, key) 528 if err != nil { 529 t.Fatalf("failed to create tx: %s", err) 530 } 531 b.AddTx(tx) 532 } 533 }, 534 expectedTip: big.NewInt(92_212_529_423), 535 }, timeCrunchOracleConfig()) 536 }