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 }