| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Copyright 2015 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package eth
- import (
- "math/big"
- "math/rand"
- "sync"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- )
- const gpoProcessPastBlocks = 100
- type blockPriceInfo struct {
- baseGasPrice *big.Int
- }
- type GasPriceOracle struct {
- eth *Ethereum
- chain *core.BlockChain
- events event.Subscription
- blocks map[uint64]*blockPriceInfo
- firstProcessed, lastProcessed uint64
- lastBaseMutex sync.Mutex
- lastBase, minBase *big.Int
- }
- func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) {
- self = &GasPriceOracle{}
- self.blocks = make(map[uint64]*blockPriceInfo)
- self.eth = eth
- self.chain = eth.blockchain
- self.events = eth.EventMux().Subscribe(
- core.ChainEvent{},
- core.ChainSplitEvent{},
- )
- minbase := new(big.Int).Mul(self.eth.GpoMinGasPrice, big.NewInt(100))
- minbase = minbase.Div(minbase, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
- self.minBase = minbase
- self.processPastBlocks()
- go self.listenLoop()
- return
- }
- func (self *GasPriceOracle) processPastBlocks() {
- last := int64(-1)
- cblock := self.chain.CurrentBlock()
- if cblock != nil {
- last = int64(cblock.NumberU64())
- }
- first := int64(0)
- if last > gpoProcessPastBlocks {
- first = last - gpoProcessPastBlocks
- }
- self.firstProcessed = uint64(first)
- for i := first; i <= last; i++ {
- block := self.chain.GetBlockByNumber(uint64(i))
- if block != nil {
- self.processBlock(block)
- }
- }
- }
- func (self *GasPriceOracle) listenLoop() {
- defer self.events.Unsubscribe()
- for event := range self.events.Chan() {
- switch event := event.Data.(type) {
- case core.ChainEvent:
- self.processBlock(event.Block)
- case core.ChainSplitEvent:
- self.processBlock(event.Block)
- }
- }
- }
- func (self *GasPriceOracle) processBlock(block *types.Block) {
- i := block.NumberU64()
- if i > self.lastProcessed {
- self.lastProcessed = i
- }
- lastBase := self.eth.GpoMinGasPrice
- bpl := self.blocks[i-1]
- if bpl != nil {
- lastBase = bpl.baseGasPrice
- }
- if lastBase == nil {
- return
- }
- var corr int
- lp := self.lowestPrice(block)
- if lp == nil {
- return
- }
- if lastBase.Cmp(lp) < 0 {
- corr = self.eth.GpobaseStepUp
- } else {
- corr = -self.eth.GpobaseStepDown
- }
- crand := int64(corr * (900 + rand.Intn(201)))
- newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand))
- newBase.Div(newBase, big.NewInt(1000000))
- if newBase.Cmp(self.minBase) < 0 {
- newBase = self.minBase
- }
- bpi := self.blocks[i]
- if bpi == nil {
- bpi = &blockPriceInfo{}
- self.blocks[i] = bpi
- }
- bpi.baseGasPrice = newBase
- self.lastBaseMutex.Lock()
- self.lastBase = newBase
- self.lastBaseMutex.Unlock()
- glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", block.NumberU64(), newBase.Int64())
- }
- // returns the lowers possible price with which a tx was or could have been included
- func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
- gasUsed := big.NewInt(0)
- receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash())
- if len(receipts) > 0 {
- if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
- gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
- }
- }
- if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
- big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 {
- // block is not full, could have posted a tx with MinGasPrice
- return big.NewInt(0)
- }
- txs := block.Transactions()
- if len(txs) == 0 {
- return big.NewInt(0)
- }
- // block is full, find smallest gasPrice
- minPrice := txs[0].GasPrice()
- for i := 1; i < len(txs); i++ {
- price := txs[i].GasPrice()
- if price.Cmp(minPrice) < 0 {
- minPrice = price
- }
- }
- return minPrice
- }
- func (self *GasPriceOracle) SuggestPrice() *big.Int {
- self.lastBaseMutex.Lock()
- base := self.lastBase
- self.lastBaseMutex.Unlock()
- if base == nil {
- base = self.eth.GpoMinGasPrice
- }
- if base == nil {
- return big.NewInt(10000000000000) // apparently MinGasPrice is not initialized during some tests
- }
- baseCorr := new(big.Int).Mul(base, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
- baseCorr.Div(baseCorr, big.NewInt(100))
- if baseCorr.Cmp(self.eth.GpoMinGasPrice) < 0 {
- return self.eth.GpoMinGasPrice
- }
- if baseCorr.Cmp(self.eth.GpoMaxGasPrice) > 0 {
- return self.eth.GpoMaxGasPrice
- }
- return baseCorr
- }
|