go-ethereum/eth/gasprice.go

207 lines
5.2 KiB
Go
Raw Permalink Normal View History

2015-07-07 02:54:22 +02:00
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
2015-07-07 02:54:22 +02:00
//
// The go-ethereum library is free software: you can redistribute it and/or modify
2015-07-07 02:54:22 +02:00
// 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,
2015-07-07 02:54:22 +02:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-07 02:54:22 +02:00
// 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/>.
2015-07-07 02:54:22 +02:00
2015-05-26 14:28:32 +02:00
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.ChainManager
events event.Subscription
blocks map[uint64]*blockPriceInfo
firstProcessed, lastProcessed uint64
lastBaseMutex sync.Mutex
2015-08-09 02:13:15 +02:00
lastBase, minBase *big.Int
2015-05-26 14:28:32 +02:00
}
func NewGasPriceOracle(eth *Ethereum) (self *GasPriceOracle) {
self = &GasPriceOracle{}
self.blocks = make(map[uint64]*blockPriceInfo)
self.eth = eth
self.chain = eth.chainManager
self.events = eth.EventMux().Subscribe(
core.ChainEvent{},
core.ChainSplitEvent{},
)
2015-08-09 02:13:15 +02:00
minbase := new(big.Int).Mul(self.eth.GpoMinGasPrice, big.NewInt(100))
minbase = minbase.Div(minbase, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
self.minBase = minbase
2015-05-26 14:28:32 +02:00
self.processPastBlocks()
go self.listenLoop()
return
}
func (self *GasPriceOracle) processPastBlocks() {
2015-06-29 13:48:10 +02:00
last := int64(-1)
cblock := self.chain.CurrentBlock()
if cblock != nil {
last = int64(cblock.NumberU64())
}
first := int64(0)
2015-05-26 14:28:32 +02:00
if last > gpoProcessPastBlocks {
first = last - gpoProcessPastBlocks
}
2015-06-29 13:48:10 +02:00
self.firstProcessed = uint64(first)
2015-05-26 14:28:32 +02:00
for i := first; i <= last; i++ {
2015-06-29 13:48:10 +02:00
block := self.chain.GetBlockByNumber(uint64(i))
if block != nil {
self.processBlock(block)
}
2015-05-26 14:28:32 +02:00
}
}
func (self *GasPriceOracle) listenLoop() {
for {
ev, isopen := <-self.events.Chan()
if !isopen {
break
}
switch ev := ev.(type) {
case core.ChainEvent:
self.processBlock(ev.Block)
case core.ChainSplitEvent:
self.processBlock(ev.Block)
}
}
self.events.Unsubscribe()
}
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))
2015-08-09 02:13:15 +02:00
if newBase.Cmp(self.minBase) < 0 {
newBase = self.minBase
}
2015-05-26 14:28:32 +02:00
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 {
2015-08-09 02:13:15 +02:00
gasUsed := big.NewInt(0)
2015-05-26 14:28:32 +02:00
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
}
2015-05-26 14:28:32 +02:00
}
if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
2015-05-26 14:28:32 +02:00
big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 {
// block is not full, could have posted a tx with MinGasPrice
2015-08-09 02:13:15 +02:00
return big.NewInt(0)
2015-05-26 14:28:32 +02:00
}
txs := block.Transactions()
if len(txs) == 0 {
2015-08-09 02:13:15 +02:00
return big.NewInt(0)
2015-05-26 14:28:32 +02:00
}
// block is full, find smallest gasPrice
minPrice := txs[0].GasPrice()
for i := 1; i < len(txs); i++ {
price := txs[i].GasPrice()
2015-05-26 14:28:32 +02:00
if price.Cmp(minPrice) < 0 {
minPrice = price
}
}
return minPrice
}
func (self *GasPriceOracle) SuggestPrice() *big.Int {
self.lastBaseMutex.Lock()
base := self.lastBase
self.lastBaseMutex.Unlock()
2015-05-26 14:39:13 +02:00
if base == nil {
base = self.eth.GpoMinGasPrice
}
if base == nil {
return big.NewInt(10000000000000) // apparently MinGasPrice is not initialized during some tests
}
2015-05-26 14:39:13 +02:00
2015-05-27 12:39:59 +02:00
baseCorr := new(big.Int).Mul(base, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
2015-05-26 14:28:32 +02:00
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
}