|
@@ -23,49 +23,66 @@ import (
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"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"
|
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-const gpoProcessPastBlocks = 100
|
|
|
|
|
|
|
+const (
|
|
|
|
|
+ gpoProcessPastBlocks = 100
|
|
|
|
|
+
|
|
|
|
|
+ // for testing
|
|
|
|
|
+ gpoDefaultBaseCorrectionFactor = 110
|
|
|
|
|
+ gpoDefaultMinGasPrice = 10000000000000
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
type blockPriceInfo struct {
|
|
type blockPriceInfo struct {
|
|
|
baseGasPrice *big.Int
|
|
baseGasPrice *big.Int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// GasPriceOracle recommends gas prices based on the content of recent
|
|
|
|
|
+// blocks.
|
|
|
type GasPriceOracle struct {
|
|
type GasPriceOracle struct {
|
|
|
- eth *Ethereum
|
|
|
|
|
- chain *core.BlockChain
|
|
|
|
|
- events event.Subscription
|
|
|
|
|
|
|
+ eth *Ethereum
|
|
|
|
|
+ initOnce sync.Once
|
|
|
|
|
+ minPrice *big.Int
|
|
|
|
|
+ lastBaseMutex sync.Mutex
|
|
|
|
|
+ lastBase *big.Int
|
|
|
|
|
+
|
|
|
|
|
+ // state of listenLoop
|
|
|
blocks map[uint64]*blockPriceInfo
|
|
blocks map[uint64]*blockPriceInfo
|
|
|
firstProcessed, lastProcessed uint64
|
|
firstProcessed, lastProcessed uint64
|
|
|
- lastBaseMutex sync.Mutex
|
|
|
|
|
- lastBase, minBase *big.Int
|
|
|
|
|
|
|
+ minBase *big.Int
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// NewGasPriceOracle returns a new oracle.
|
|
|
|
|
+func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle {
|
|
|
|
|
+ minprice := eth.GpoMinGasPrice
|
|
|
|
|
+ if minprice == nil {
|
|
|
|
|
+ minprice = big.NewInt(gpoDefaultMinGasPrice)
|
|
|
|
|
+ }
|
|
|
|
|
+ minbase := new(big.Int).Mul(minprice, big.NewInt(100))
|
|
|
|
|
+ if eth.GpobaseCorrectionFactor > 0 {
|
|
|
|
|
+ minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor)))
|
|
|
|
|
+ }
|
|
|
|
|
+ return &GasPriceOracle{
|
|
|
|
|
+ eth: eth,
|
|
|
|
|
+ blocks: make(map[uint64]*blockPriceInfo),
|
|
|
|
|
+ minBase: minbase,
|
|
|
|
|
+ minPrice: minprice,
|
|
|
|
|
+ lastBase: minprice,
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-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 (gpo *GasPriceOracle) init() {
|
|
|
|
|
+ gpo.initOnce.Do(func() {
|
|
|
|
|
+ gpo.processPastBlocks(gpo.eth.BlockChain())
|
|
|
|
|
+ go gpo.listenLoop()
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (self *GasPriceOracle) processPastBlocks() {
|
|
|
|
|
|
|
+func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
|
|
|
last := int64(-1)
|
|
last := int64(-1)
|
|
|
- cblock := self.chain.CurrentBlock()
|
|
|
|
|
|
|
+ cblock := chain.CurrentBlock()
|
|
|
if cblock != nil {
|
|
if cblock != nil {
|
|
|
last = int64(cblock.NumberU64())
|
|
last = int64(cblock.NumberU64())
|
|
|
}
|
|
}
|
|
@@ -75,7 +92,7 @@ func (self *GasPriceOracle) processPastBlocks() {
|
|
|
}
|
|
}
|
|
|
self.firstProcessed = uint64(first)
|
|
self.firstProcessed = uint64(first)
|
|
|
for i := first; i <= last; i++ {
|
|
for i := first; i <= last; i++ {
|
|
|
- block := self.chain.GetBlockByNumber(uint64(i))
|
|
|
|
|
|
|
+ block := chain.GetBlockByNumber(uint64(i))
|
|
|
if block != nil {
|
|
if block != nil {
|
|
|
self.processBlock(block)
|
|
self.processBlock(block)
|
|
|
}
|
|
}
|
|
@@ -84,9 +101,10 @@ func (self *GasPriceOracle) processPastBlocks() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (self *GasPriceOracle) listenLoop() {
|
|
func (self *GasPriceOracle) listenLoop() {
|
|
|
- defer self.events.Unsubscribe()
|
|
|
|
|
|
|
+ events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{})
|
|
|
|
|
+ defer events.Unsubscribe()
|
|
|
|
|
|
|
|
- for event := range self.events.Chan() {
|
|
|
|
|
|
|
+ for event := range events.Chan() {
|
|
|
switch event := event.Data.(type) {
|
|
switch event := event.Data.(type) {
|
|
|
case core.ChainEvent:
|
|
case core.ChainEvent:
|
|
|
self.processBlock(event.Block)
|
|
self.processBlock(event.Block)
|
|
@@ -102,7 +120,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
|
|
|
self.lastProcessed = i
|
|
self.lastProcessed = i
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- lastBase := self.eth.GpoMinGasPrice
|
|
|
|
|
|
|
+ lastBase := self.minPrice
|
|
|
bpl := self.blocks[i-1]
|
|
bpl := self.blocks[i-1]
|
|
|
if bpl != nil {
|
|
if bpl != nil {
|
|
|
lastBase = bpl.baseGasPrice
|
|
lastBase = bpl.baseGasPrice
|
|
@@ -176,28 +194,19 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
|
|
|
return minPrice
|
|
return minPrice
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// SuggestPrice returns the recommended gas price.
|
|
|
func (self *GasPriceOracle) SuggestPrice() *big.Int {
|
|
func (self *GasPriceOracle) SuggestPrice() *big.Int {
|
|
|
|
|
+ self.init()
|
|
|
self.lastBaseMutex.Lock()
|
|
self.lastBaseMutex.Lock()
|
|
|
- base := self.lastBase
|
|
|
|
|
|
|
+ price := new(big.Int).Set(self.lastBase)
|
|
|
self.lastBaseMutex.Unlock()
|
|
self.lastBaseMutex.Unlock()
|
|
|
|
|
|
|
|
- if base == nil {
|
|
|
|
|
- base = self.eth.GpoMinGasPrice
|
|
|
|
|
|
|
+ price.Mul(price, big.NewInt(int64(self.eth.GpobaseCorrectionFactor)))
|
|
|
|
|
+ price.Div(price, big.NewInt(100))
|
|
|
|
|
+ if price.Cmp(self.minPrice) < 0 {
|
|
|
|
|
+ price.Set(self.minPrice)
|
|
|
|
|
+ } else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 {
|
|
|
|
|
+ price.Set(self.eth.GpoMaxGasPrice)
|
|
|
}
|
|
}
|
|
|
- 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
|
|
|
|
|
|
|
+ return price
|
|
|
}
|
|
}
|