github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/gasprice/gasprice_test.go (about)

     1  package gasprice
     2  
     3  import (
     4  	"math/big"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/unicornultrafoundation/go-u2u/common"
    10  	"github.com/unicornultrafoundation/go-u2u/common/math"
    11  	"github.com/unicornultrafoundation/go-u2u/core/types"
    12  
    13  	"github.com/unicornultrafoundation/go-helios/native/idx"
    14  	"github.com/unicornultrafoundation/go-u2u/u2u"
    15  )
    16  
    17  type fakeTx struct {
    18  	gas uint64
    19  	tip *big.Int
    20  	cap *big.Int
    21  }
    22  
    23  type TestBackend struct {
    24  	block             idx.Block
    25  	totalGasPowerLeft uint64
    26  	rules             u2u.Rules
    27  	pendingRules      u2u.Rules
    28  	pendingTxs        []fakeTx
    29  }
    30  
    31  func (t TestBackend) GetLatestBlockIndex() idx.Block {
    32  	return t.block
    33  }
    34  
    35  func (t TestBackend) TotalGasPowerLeft() uint64 {
    36  	return t.totalGasPowerLeft
    37  }
    38  
    39  func (t TestBackend) GetRules() u2u.Rules {
    40  	return t.rules
    41  }
    42  
    43  func (t TestBackend) GetPendingRules() u2u.Rules {
    44  	return t.pendingRules
    45  }
    46  
    47  func (t TestBackend) PendingTxs() map[common.Address]types.Transactions {
    48  	txs := make(map[common.Address]types.Transactions, len(t.pendingTxs))
    49  	for i, tx := range t.pendingTxs {
    50  		txs[common.BytesToAddress(big.NewInt(int64(i)).Bytes())] = types.Transactions{
    51  			types.NewTx(&types.DynamicFeeTx{
    52  				Nonce:     uint64(i),
    53  				GasTipCap: tx.tip,
    54  				GasFeeCap: tx.cap,
    55  				Gas:       tx.gas,
    56  			}),
    57  		}
    58  	}
    59  	return txs
    60  }
    61  
    62  func TestOracle_EffectiveMinGasPrice(t *testing.T) {
    63  	backend := &TestBackend{
    64  		block:             1,
    65  		totalGasPowerLeft: 0,
    66  		rules:             u2u.FakeNetRules(),
    67  		pendingRules:      u2u.FakeNetRules(),
    68  	}
    69  
    70  	gpo := NewOracle(Config{})
    71  	gpo.cfg.MaxGasPrice = math.MaxBig256
    72  	gpo.cfg.MinGasPrice = new(big.Int)
    73  
    74  	// no backend
    75  	require.Equal(t, "0", gpo.EffectiveMinGasPrice().String())
    76  	gpo.backend = backend
    77  
    78  	// all the gas is consumed, price should be high
    79  	backend.block++
    80  	backend.totalGasPowerLeft = 0
    81  	require.Equal(t, "25000000000", gpo.EffectiveMinGasPrice().String())
    82  
    83  	// test the cache as well
    84  	backend.totalGasPowerLeft = 1008000000
    85  	require.Equal(t, "25000000000", gpo.EffectiveMinGasPrice().String())
    86  	backend.block++
    87  	require.Equal(t, "24994672000", gpo.EffectiveMinGasPrice().String())
    88  	backend.block++
    89  
    90  	// all the gas is free, price should be low
    91  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64()
    92  	require.Equal(t, uint64(0x92aeed1c000), backend.totalGasPowerLeft)
    93  	require.Equal(t, "1000000000", gpo.EffectiveMinGasPrice().String())
    94  	backend.block++
    95  
    96  	// edge case with totalGasPowerLeft exceeding maxTotalGasPower
    97  	backend.totalGasPowerLeft = 2 * gpo.maxTotalGasPower().Uint64()
    98  	require.Equal(t, "1000000000", gpo.EffectiveMinGasPrice().String())
    99  	backend.block++
   100  
   101  	// half of the gas is free, price should be 3.75x
   102  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64() / 2
   103  	require.Equal(t, "3750000000", gpo.EffectiveMinGasPrice().String())
   104  	backend.block++
   105  
   106  	// third of the gas is free, price should be higher
   107  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64() / 3
   108  	require.Equal(t, "8125008000", gpo.EffectiveMinGasPrice().String())
   109  	backend.block++
   110  
   111  	// check min and max price hard limits don't apply
   112  	gpo.cfg.MaxGasPrice = big.NewInt(2000000000)
   113  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64() / 3
   114  	require.Equal(t, "8125008000", gpo.EffectiveMinGasPrice().String())
   115  	backend.block++
   116  
   117  	gpo.cfg.MinGasPrice = big.NewInt(1500000000)
   118  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64()
   119  	require.Equal(t, "1000000000", gpo.EffectiveMinGasPrice().String())
   120  	backend.block++
   121  }
   122  
   123  func TestOracle_constructiveGasPrice(t *testing.T) {
   124  	backend := &TestBackend{
   125  		totalGasPowerLeft: 0,
   126  		rules:             u2u.FakeNetRules(),
   127  		pendingRules:      u2u.FakeNetRules(),
   128  	}
   129  
   130  	gpo := NewOracle(Config{})
   131  	gpo.backend = backend
   132  	gpo.cfg.MaxGasPrice = math.MaxBig256
   133  	gpo.cfg.MinGasPrice = new(big.Int)
   134  
   135  	// all the gas is consumed, price should be high
   136  	backend.totalGasPowerLeft = 0
   137  	require.Equal(t, "2500", gpo.constructiveGasPrice(0, 0, big.NewInt(100)).String())
   138  	require.Equal(t, "2500", gpo.constructiveGasPrice(0, 0.1*DecimalUnit, big.NewInt(100)).String())
   139  	require.Equal(t, "2500", gpo.constructiveGasPrice(1008000000, 0, big.NewInt(100)).String())
   140  	require.Equal(t, "2500", gpo.constructiveGasPrice(gpo.maxTotalGasPower().Uint64()*2, 2*DecimalUnit, big.NewInt(100)).String())
   141  
   142  	// all the gas is free, price should be low
   143  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64()
   144  	require.Equal(t, "100", gpo.constructiveGasPrice(0, 0, big.NewInt(100)).String())
   145  	require.Equal(t, "120", gpo.constructiveGasPrice(0, 0.1*DecimalUnit, big.NewInt(100)).String())
   146  	require.Equal(t, "101", gpo.constructiveGasPrice(100800000000, 0, big.NewInt(100)).String())
   147  	require.Equal(t, "2500", gpo.constructiveGasPrice(gpo.maxTotalGasPower().Uint64()*2, 2*DecimalUnit, big.NewInt(100)).String())
   148  
   149  	// half of the gas is free, price should be 3.75x
   150  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64() / 2
   151  	require.Equal(t, "375", gpo.constructiveGasPrice(0, 0, big.NewInt(100)).String())
   152  	require.Equal(t, "637", gpo.constructiveGasPrice(0, 0.1*DecimalUnit, big.NewInt(100)).String())
   153  	require.Equal(t, "401", gpo.constructiveGasPrice(100800000000, 0, big.NewInt(100)).String())
   154  	require.Equal(t, "2500", gpo.constructiveGasPrice(gpo.maxTotalGasPower().Uint64()*2, 2*DecimalUnit, big.NewInt(100)).String())
   155  
   156  	// third of the gas is free, price should be higher
   157  	backend.totalGasPowerLeft = gpo.maxTotalGasPower().Uint64() / 3
   158  	require.Equal(t, "812", gpo.constructiveGasPrice(0, 0, big.NewInt(100)).String())
   159  	require.Equal(t, "1255", gpo.constructiveGasPrice(0, 0.1*DecimalUnit, big.NewInt(100)).String())
   160  	require.Equal(t, "838", gpo.constructiveGasPrice(100800000000, 0, big.NewInt(100)).String())
   161  	require.Equal(t, "2500", gpo.constructiveGasPrice(gpo.maxTotalGasPower().Uint64()*2, 2*DecimalUnit, big.NewInt(100)).String())
   162  
   163  }
   164  
   165  func TestOracle_reactiveGasPrice(t *testing.T) {
   166  	backend := &TestBackend{
   167  		totalGasPowerLeft: 0,
   168  		rules:             u2u.FakeNetRules(),
   169  		pendingRules:      u2u.FakeNetRules(),
   170  	}
   171  
   172  	gpo := NewOracle(Config{})
   173  	gpo.backend = backend
   174  	gpo.cfg.MaxGasPrice = math.MaxBig256
   175  	gpo.cfg.MinGasPrice = new(big.Int)
   176  
   177  	// no stats -> zero price
   178  	gpo.c = circularTxpoolStats{}
   179  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   180  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   181  	gpo.txpoolStatsTick()
   182  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   183  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   184  
   185  	// one tx
   186  	gpo.c = circularTxpoolStats{}
   187  	backend.pendingTxs = append(backend.pendingTxs, fakeTx{
   188  		gas: 50000,
   189  		tip: big.NewInt(0),
   190  		cap: big.NewInt(1e9),
   191  	})
   192  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   193  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   194  	gpo.txpoolStatsTick()
   195  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   196  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   197  	require.Equal(t, "200000000", gpo.reactiveGasPrice(0.9*DecimalUnit).String())
   198  	require.Equal(t, "600000000", gpo.reactiveGasPrice(0.95*DecimalUnit).String())
   199  	require.Equal(t, "920000000", gpo.reactiveGasPrice(0.99*DecimalUnit).String())
   200  	require.Equal(t, "1000000000", gpo.reactiveGasPrice(DecimalUnit).String())
   201  
   202  	// add one more tx
   203  	backend.pendingTxs = append(backend.pendingTxs, fakeTx{
   204  		gas: 25000,
   205  		tip: big.NewInt(3 * 1e9),
   206  		cap: big.NewInt(3.5 * 1e9),
   207  	})
   208  
   209  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   210  	require.Equal(t, "1000000000", gpo.reactiveGasPrice(DecimalUnit).String())
   211  	gpo.txpoolStatsTick()
   212  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   213  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   214  	require.Equal(t, "450000000", gpo.reactiveGasPrice(0.9*DecimalUnit).String())
   215  	require.Equal(t, "1350000000", gpo.reactiveGasPrice(0.95*DecimalUnit).String())
   216  	require.Equal(t, "2070000000", gpo.reactiveGasPrice(0.99*DecimalUnit).String())
   217  	require.Equal(t, "2250000000", gpo.reactiveGasPrice(DecimalUnit).String())
   218  
   219  	// add two more txs
   220  	backend.pendingTxs = append(backend.pendingTxs, fakeTx{
   221  		gas: 2500000,
   222  		tip: big.NewInt(1 * 1e9),
   223  		cap: big.NewInt(3.5 * 1e9),
   224  	})
   225  	backend.pendingTxs = append(backend.pendingTxs, fakeTx{
   226  		gas: 2500000,
   227  		tip: big.NewInt(0 * 1e9),
   228  		cap: big.NewInt(3.5 * 1e9),
   229  	})
   230  
   231  	gpo.txpoolStatsTick()
   232  	require.Equal(t, "0", gpo.reactiveGasPrice(0).String())
   233  	require.Equal(t, "333333333", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   234  	require.Equal(t, "799999999", gpo.reactiveGasPrice(0.9*DecimalUnit).String())
   235  	require.Equal(t, "1733333332", gpo.reactiveGasPrice(0.95*DecimalUnit).String())
   236  	require.Equal(t, "2479999999", gpo.reactiveGasPrice(0.99*DecimalUnit).String())
   237  	require.Equal(t, "2666666666", gpo.reactiveGasPrice(DecimalUnit).String())
   238  	// price gets closer to latest state
   239  	gpo.txpoolStatsTick()
   240  	require.Equal(t, "500000000", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   241  	require.Equal(t, "2875000000", gpo.reactiveGasPrice(DecimalUnit).String())
   242  	gpo.txpoolStatsTick()
   243  	require.Equal(t, "600000000", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   244  	require.Equal(t, "3000000000", gpo.reactiveGasPrice(DecimalUnit).String())
   245  	gpo.txpoolStatsTick()
   246  	require.Equal(t, "666666666", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   247  	require.Equal(t, "3083333333", gpo.reactiveGasPrice(DecimalUnit).String())
   248  	for i := 0; i < statsBuffer-5; i++ {
   249  		gpo.txpoolStatsTick()
   250  	}
   251  	require.Equal(t, "933333333", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   252  	require.Equal(t, "3500000000", gpo.reactiveGasPrice(DecimalUnit).String())
   253  	gpo.txpoolStatsTick()
   254  	require.Equal(t, "1000000000", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   255  	require.Equal(t, "3500000000", gpo.reactiveGasPrice(DecimalUnit).String())
   256  	gpo.txpoolStatsTick()
   257  	require.Equal(t, "1000000000", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   258  	require.Equal(t, "3500000000", gpo.reactiveGasPrice(DecimalUnit).String())
   259  
   260  	// change minGasPrice
   261  	backend.rules.Economy.MinGasPrice = big.NewInt(100)
   262  	gpo.txpoolStatsTick()
   263  	require.Equal(t, "933333340", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   264  	require.Equal(t, "3466666673", gpo.reactiveGasPrice(DecimalUnit).String())
   265  	gpo.txpoolStatsTick()
   266  	require.Equal(t, "866666680", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   267  	require.Equal(t, "3433333346", gpo.reactiveGasPrice(DecimalUnit).String())
   268  	gpo.txpoolStatsTick()
   269  	require.Equal(t, "800000020", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   270  	require.Equal(t, "3400000020", gpo.reactiveGasPrice(DecimalUnit).String())
   271  	gpo.txpoolStatsTick()
   272  	// recent gas price plus 5%
   273  	require.Equal(t, "105", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   274  	require.Equal(t, "3150000105", gpo.reactiveGasPrice(DecimalUnit).String())
   275  	for i := 0; i < statsBuffer-5; i++ {
   276  		gpo.txpoolStatsTick()
   277  	}
   278  	require.Equal(t, "105", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   279  	require.Equal(t, "3033333426", gpo.reactiveGasPrice(DecimalUnit).String())
   280  	gpo.txpoolStatsTick()
   281  	require.Equal(t, "100", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   282  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   283  	gpo.txpoolStatsTick()
   284  	require.Equal(t, "100", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   285  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   286  
   287  	// half of txs are confirmed now
   288  	backend.pendingTxs = backend.pendingTxs[:2]
   289  	gpo.txpoolStatsTick()
   290  	require.Equal(t, "93", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   291  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   292  	gpo.txpoolStatsTick()
   293  	require.Equal(t, "86", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   294  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   295  	for i := 0; i < statsBuffer-3; i++ {
   296  		gpo.txpoolStatsTick()
   297  	}
   298  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   299  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   300  	gpo.txpoolStatsTick()
   301  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   302  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   303  	gpo.txpoolStatsTick()
   304  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   305  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   306  
   307  	// all txs are confirmed now
   308  	backend.pendingTxs = backend.pendingTxs[:0]
   309  	gpo.txpoolStatsTick()
   310  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   311  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   312  	gpo.txpoolStatsTick()
   313  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   314  	require.Equal(t, "3000000100", gpo.reactiveGasPrice(DecimalUnit).String())
   315  	for i := 0; i < statsBuffer-3; i++ {
   316  		gpo.txpoolStatsTick()
   317  	}
   318  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   319  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   320  	gpo.txpoolStatsTick()
   321  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   322  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   323  	gpo.txpoolStatsTick()
   324  	require.Equal(t, "0", gpo.reactiveGasPrice(0.8*DecimalUnit).String())
   325  	require.Equal(t, "0", gpo.reactiveGasPrice(DecimalUnit).String())
   326  }