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