github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/eth/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 eth 18 19 import ( 20 "math/big" 21 "math/rand" 22 "sync" 23 24 "github.com/ethereumproject/go-ethereum/core" 25 "github.com/ethereumproject/go-ethereum/core/types" 26 "github.com/ethereumproject/go-ethereum/logger" 27 "github.com/ethereumproject/go-ethereum/logger/glog" 28 ) 29 30 const ( 31 gpoProcessPastBlocks = 100 32 33 gpoDefaultMinGasPrice = 10000000000000 34 ) 35 36 type blockPriceInfo struct { 37 baseGasPrice *big.Int 38 } 39 40 // GasPriceOracle recommends gas prices based on the content of recent 41 // blocks. 42 type GasPriceOracle struct { 43 eth *Ethereum 44 initOnce sync.Once 45 minPrice *big.Int 46 lastBaseMutex sync.Mutex 47 lastBase *big.Int 48 49 // state of listenLoop 50 blocks map[uint64]*blockPriceInfo 51 firstProcessed, lastProcessed uint64 52 minBase *big.Int 53 } 54 55 // NewGasPriceOracle returns a new oracle. 56 func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { 57 minprice := eth.GpoMinGasPrice 58 if minprice == nil { 59 minprice = big.NewInt(gpoDefaultMinGasPrice) 60 } 61 minbase := new(big.Int).Mul(minprice, big.NewInt(100)) 62 if eth.GpobaseCorrectionFactor > 0 { 63 minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor))) 64 } 65 return &GasPriceOracle{ 66 eth: eth, 67 blocks: make(map[uint64]*blockPriceInfo), 68 minBase: minbase, 69 minPrice: minprice, 70 lastBase: minprice, 71 } 72 } 73 74 func (gpo *GasPriceOracle) init() { 75 gpo.initOnce.Do(func() { 76 gpo.processPastBlocks(gpo.eth.BlockChain()) 77 go gpo.listenLoop() 78 }) 79 } 80 81 func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { 82 last := int64(-1) 83 cblock := chain.CurrentBlock() 84 if cblock != nil { 85 last = int64(cblock.NumberU64()) 86 } 87 first := int64(0) 88 if last > gpoProcessPastBlocks { 89 first = last - gpoProcessPastBlocks 90 } 91 self.firstProcessed = uint64(first) 92 for i := first; i <= last; i++ { 93 block := chain.GetBlockByNumber(uint64(i)) 94 if block != nil { 95 self.processBlock(block) 96 } 97 } 98 99 } 100 101 func (self *GasPriceOracle) listenLoop() { 102 events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) 103 defer events.Unsubscribe() 104 105 for event := range events.Chan() { 106 switch event := event.Data.(type) { 107 case core.ChainEvent: 108 self.processBlock(event.Block) 109 case core.ChainSplitEvent: 110 self.processBlock(event.Block) 111 } 112 } 113 } 114 115 func (self *GasPriceOracle) processBlock(block *types.Block) { 116 i := block.NumberU64() 117 if i > self.lastProcessed { 118 self.lastProcessed = i 119 } 120 121 lastBase := self.minPrice 122 bpl := self.blocks[i-1] 123 if bpl != nil { 124 lastBase = bpl.baseGasPrice 125 } 126 if lastBase == nil { 127 return 128 } 129 130 var corr int 131 lp := self.lowestPrice(block) 132 if lp == nil { 133 return 134 } 135 136 if lastBase.Cmp(lp) < 0 { 137 corr = self.eth.GpobaseStepUp 138 } else { 139 corr = -self.eth.GpobaseStepDown 140 } 141 142 crand := int64(corr * (900 + rand.Intn(201))) 143 newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand)) 144 newBase.Div(newBase, big.NewInt(1000000)) 145 146 if newBase.Cmp(self.minBase) < 0 { 147 newBase = self.minBase 148 } 149 150 bpi := self.blocks[i] 151 if bpi == nil { 152 bpi = &blockPriceInfo{} 153 self.blocks[i] = bpi 154 } 155 bpi.baseGasPrice = newBase 156 self.lastBaseMutex.Lock() 157 self.lastBase = newBase 158 self.lastBaseMutex.Unlock() 159 160 glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", block.NumberU64(), newBase.Int64()) 161 } 162 163 // returns the lowers possible price with which a tx was or could have been included 164 func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { 165 gasUsed := big.NewInt(0) 166 167 receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash()) 168 if len(receipts) > 0 { 169 if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { 170 gasUsed = receipts[len(receipts)-1].CumulativeGasUsed 171 } 172 } 173 174 if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(), 175 big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 { 176 // block is not full, could have posted a tx with MinGasPrice 177 return big.NewInt(0) 178 } 179 180 txs := block.Transactions() 181 if len(txs) == 0 { 182 return big.NewInt(0) 183 } 184 // block is full, find smallest gasPrice 185 minPrice := txs[0].GasPrice() 186 for i := 1; i < len(txs); i++ { 187 price := txs[i].GasPrice() 188 if price.Cmp(minPrice) < 0 { 189 minPrice = price 190 } 191 } 192 return minPrice 193 } 194 195 // SuggestPrice returns the recommended gas price. 196 func (self *GasPriceOracle) SuggestPrice() *big.Int { 197 self.init() 198 self.lastBaseMutex.Lock() 199 price := new(big.Int).Set(self.lastBase) 200 self.lastBaseMutex.Unlock() 201 202 price.Mul(price, big.NewInt(int64(self.eth.GpobaseCorrectionFactor))) 203 price.Div(price, big.NewInt(100)) 204 if price.Cmp(self.minPrice) < 0 { 205 price.Set(self.minPrice) 206 } else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 { 207 price.Set(self.eth.GpoMaxGasPrice) 208 } 209 return price 210 }