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  }