github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/gasprice/gasprice.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package gasprice 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync" 23 "time" 24 25 lru "github.com/hashicorp/golang-lru" 26 27 "github.com/unicornultrafoundation/go-u2u/common" 28 "github.com/unicornultrafoundation/go-u2u/common/math" 29 "github.com/unicornultrafoundation/go-u2u/core/types" 30 "github.com/unicornultrafoundation/go-u2u/log" 31 "github.com/unicornultrafoundation/go-u2u/params" 32 33 "github.com/unicornultrafoundation/go-helios/native/idx" 34 "github.com/unicornultrafoundation/go-helios/utils/piecefunc" 35 36 "github.com/unicornultrafoundation/go-u2u/u2u" 37 ) 38 39 var ( 40 DefaultMaxGasPrice = big.NewInt(10000000 * params.GWei) 41 DecimalUnitBn = big.NewInt(DecimalUnit) 42 secondBn = new(big.Int).SetUint64(uint64(time.Second)) 43 ) 44 45 const ( 46 AsDefaultCertainty = math.MaxUint64 47 DecimalUnit = piecefunc.DecimalUnit 48 ) 49 50 type Config struct { 51 MaxGasPrice *big.Int `toml:",omitempty"` 52 MinGasPrice *big.Int `toml:",omitempty"` 53 MinGasTip *big.Int `toml:",omitempty"` 54 DefaultCertainty uint64 `toml:",omitempty"` 55 } 56 57 type Reader interface { 58 GetLatestBlockIndex() idx.Block 59 TotalGasPowerLeft() uint64 60 GetRules() u2u.Rules 61 GetPendingRules() u2u.Rules 62 PendingTxs() map[common.Address]types.Transactions 63 } 64 65 type tipCache struct { 66 upd time.Time 67 tip *big.Int 68 } 69 70 type effectiveMinGasPriceCache struct { 71 head idx.Block 72 lock sync.RWMutex 73 value *big.Int 74 } 75 76 // Oracle recommends gas prices based on the content of recent 77 // blocks. Suitable for both light and full clients. 78 type Oracle struct { 79 backend Reader 80 81 c circularTxpoolStats 82 83 cfg Config 84 85 eCache effectiveMinGasPriceCache 86 tCache *lru.Cache 87 88 wg sync.WaitGroup 89 quit chan struct{} 90 } 91 92 func sanitizeBigInt(val, min, max, _default *big.Int, name string) *big.Int { 93 if val == nil || (val.Sign() == 0 && _default.Sign() != 0) { 94 log.Warn(fmt.Sprintf("Sanitizing invalid parameter %s of gasprice oracle", name), "provided", val, "updated", _default) 95 return _default 96 } 97 if min != nil && val.Cmp(min) < 0 { 98 log.Warn(fmt.Sprintf("Sanitizing invalid parameter %s of gasprice oracle", name), "provided", val, "updated", min) 99 return min 100 } 101 if max != nil && val.Cmp(max) > 0 { 102 log.Warn(fmt.Sprintf("Sanitizing invalid parameter %s of gasprice oracle", name), "provided", val, "updated", max) 103 return max 104 } 105 return val 106 } 107 108 // NewOracle returns a new gasprice oracle which can recommend suitable 109 // gasprice for newly created transaction. 110 func NewOracle(params Config) *Oracle { 111 params.MaxGasPrice = sanitizeBigInt(params.MaxGasPrice, nil, nil, DefaultMaxGasPrice, "MaxGasPrice") 112 params.MinGasPrice = sanitizeBigInt(params.MinGasPrice, nil, params.MaxGasPrice, new(big.Int), "MinGasPrice") 113 params.MinGasTip = sanitizeBigInt(params.MinGasTip, nil, new(big.Int).Sub(params.MaxGasPrice, params.MinGasPrice), new(big.Int), "MinGasTip") 114 params.DefaultCertainty = sanitizeBigInt(new(big.Int).SetUint64(params.DefaultCertainty), big.NewInt(0), DecimalUnitBn, big.NewInt(DecimalUnit/2), "DefaultCertainty").Uint64() 115 tCache, _ := lru.New(100) 116 return &Oracle{ 117 cfg: params, 118 tCache: tCache, 119 quit: make(chan struct{}), 120 } 121 } 122 123 func (gpo *Oracle) Start(backend Reader) { 124 gpo.backend = backend 125 gpo.wg.Add(1) 126 go func() { 127 defer gpo.wg.Done() 128 gpo.txpoolStatsLoop() 129 }() 130 } 131 132 func (gpo *Oracle) Stop() { 133 close(gpo.quit) 134 gpo.wg.Wait() 135 } 136 137 func (gpo *Oracle) suggestTip(certainty uint64) *big.Int { 138 minPrice := gpo.backend.GetRules().Economy.MinGasPrice 139 pendingMinPrice := gpo.backend.GetPendingRules().Economy.MinGasPrice 140 adjustedMinGasPrice := math.BigMax(minPrice, pendingMinPrice) 141 142 reactive := gpo.reactiveGasPrice(certainty) 143 constructive := gpo.constructiveGasPrice(gpo.c.totalGas(), 0.005*DecimalUnit+certainty/25, adjustedMinGasPrice) 144 145 combined := math.BigMax(reactive, constructive) 146 if combined.Cmp(gpo.cfg.MinGasPrice) < 0 { 147 combined = gpo.cfg.MinGasPrice 148 } 149 if combined.Cmp(gpo.cfg.MaxGasPrice) > 0 { 150 combined = gpo.cfg.MaxGasPrice 151 } 152 153 tip := new(big.Int).Sub(combined, minPrice) 154 if tip.Cmp(gpo.cfg.MinGasTip) < 0 { 155 return new(big.Int).Set(gpo.cfg.MinGasTip) 156 } 157 return tip 158 } 159 160 // SuggestTip returns a tip cap so that newly created transaction can have a 161 // very high chance to be included in the following blocks. 162 // 163 // Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be 164 // necessary to add the basefee to the returned number to fall back to the legacy 165 // behavior. 166 func (gpo *Oracle) SuggestTip(certainty uint64) *big.Int { 167 if gpo.backend == nil { 168 return new(big.Int) 169 } 170 if certainty == AsDefaultCertainty { 171 certainty = gpo.cfg.DefaultCertainty 172 } 173 174 const cacheSlack = DecimalUnit * 0.05 175 roundedCertainty := certainty / cacheSlack 176 if cached, ok := gpo.tCache.Get(roundedCertainty); ok { 177 cache := cached.(tipCache) 178 if time.Since(cache.upd) < statUpdatePeriod { 179 return new(big.Int).Set(cache.tip) 180 } else { 181 gpo.tCache.Remove(roundedCertainty) 182 } 183 } 184 185 tip := gpo.suggestTip(certainty) 186 187 gpo.tCache.Add(roundedCertainty, tipCache{ 188 upd: time.Now(), 189 tip: tip, 190 }) 191 return new(big.Int).Set(tip) 192 } 193 194 // EffectiveMinGasPrice returns softly enforced minimum gas price on top of on-chain minimum gas price (base fee) 195 func (gpo *Oracle) EffectiveMinGasPrice() *big.Int { 196 if gpo.backend == nil { 197 return new(big.Int).Set(gpo.cfg.MinGasPrice) 198 } 199 head := gpo.backend.GetLatestBlockIndex() 200 201 // If the latest gasprice is still available, return it. 202 gpo.eCache.lock.RLock() 203 cachedHead, cachedValue := gpo.eCache.head, gpo.eCache.value 204 gpo.eCache.lock.RUnlock() 205 if head <= cachedHead { 206 return new(big.Int).Set(cachedValue) 207 } 208 209 value := gpo.effectiveMinGasPrice() 210 211 gpo.eCache.lock.Lock() 212 if head > gpo.eCache.head { 213 gpo.eCache.head = head 214 gpo.eCache.value = value 215 } 216 gpo.eCache.lock.Unlock() 217 return new(big.Int).Set(value) 218 }