Commit a2f23ca9 authored by Péter Szilágyi's avatar Péter Szilágyi Committed by Felix Lange

cmd, core, eth, miner: remove txpool gas price limits (#14442)

parent e2015817
...@@ -237,7 +237,7 @@ var ( ...@@ -237,7 +237,7 @@ var (
GasPriceFlag = BigFlag{ GasPriceFlag = BigFlag{
Name: "gasprice", Name: "gasprice",
Usage: "Minimal gas price to accept for mining a transactions", Usage: "Minimal gas price to accept for mining a transactions",
Value: big.NewInt(20 * params.Shannon), Value: eth.DefaultConfig.GasPrice,
} }
ExtraDataFlag = cli.StringFlag{ ExtraDataFlag = cli.StringFlag{
Name: "extradata", Name: "extradata",
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
package core package core
import ( import (
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
...@@ -67,8 +65,6 @@ type ChainUncleEvent struct { ...@@ -67,8 +65,6 @@ type ChainUncleEvent struct {
type ChainHeadEvent struct{ Block *types.Block } type ChainHeadEvent struct{ Block *types.Block }
type GasPriceChanged struct{ Price *big.Int }
// Mining operation events // Mining operation events
type StartMining struct{} type StartMining struct{}
type TopMining struct{} type TopMining struct{}
...@@ -22,7 +22,9 @@ import ( ...@@ -22,7 +22,9 @@ import (
"math/big" "math/big"
"sort" "sort"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
) )
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
...@@ -53,11 +55,11 @@ type txSortedMap struct { ...@@ -53,11 +55,11 @@ type txSortedMap struct {
cache types.Transactions // Cache of the transactions already sorted cache types.Transactions // Cache of the transactions already sorted
} }
// newTxSortedMap creates a new sorted transaction map. // newTxSortedMap creates a new nonce-sorted transaction map.
func newTxSortedMap() *txSortedMap { func newTxSortedMap() *txSortedMap {
return &txSortedMap{ return &txSortedMap{
items: make(map[uint64]*types.Transaction), items: make(map[uint64]*types.Transaction),
index: &nonceHeap{}, index: new(nonceHeap),
} }
} }
...@@ -233,6 +235,12 @@ func newTxList(strict bool) *txList { ...@@ -233,6 +235,12 @@ func newTxList(strict bool) *txList {
} }
} }
// Overlaps returns whether the transaction specified has the same nonce as one
// already contained within the list.
func (l *txList) Overlaps(tx *types.Transaction) bool {
return l.txs.Get(tx.Nonce()) != nil
}
// Add tries to insert a new transaction into the list, returning whether the // Add tries to insert a new transaction into the list, returning whether the
// transaction was accepted, and if yes, any previous transaction it replaced. // transaction was accepted, and if yes, any previous transaction it replaced.
// //
...@@ -241,9 +249,12 @@ func newTxList(strict bool) *txList { ...@@ -241,9 +249,12 @@ func newTxList(strict bool) *txList {
func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) { func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) {
// If there's an older better transaction, abort // If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce()) old := l.txs.Get(tx.Nonce())
if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 { if old != nil {
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+minPriceBumpPercent)), big.NewInt(100))
if threshold.Cmp(tx.GasPrice()) >= 0 {
return false, nil return false, nil
} }
}
// Otherwise overwrite the old transaction with the current one // Otherwise overwrite the old transaction with the current one
l.txs.Put(tx) l.txs.Put(tx)
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
...@@ -340,3 +351,150 @@ func (l *txList) Empty() bool { ...@@ -340,3 +351,150 @@ func (l *txList) Empty() bool {
func (l *txList) Flatten() types.Transactions { func (l *txList) Flatten() types.Transactions {
return l.txs.Flatten() return l.txs.Flatten()
} }
// priceHeap is a heap.Interface implementation over transactions for retrieving
// price-sorted transactions to discard when the pool fills up.
type priceHeap []*types.Transaction
func (h priceHeap) Len() int { return len(h) }
func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 }
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *priceHeap) Push(x interface{}) {
*h = append(*h, x.(*types.Transaction))
}
func (h *priceHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
// txPricedList is a price-sorted heap to allow operating on transactions pool
// contents in a price-incrementing way.
type txPricedList struct {
all *map[common.Hash]*types.Transaction // Pointer to the map of all transactions
items *priceHeap // Heap of prices of all the stored transactions
stales int // Number of stale price points to (re-heap trigger)
}
// newTxPricedList creates a new price-sorted transaction heap.
func newTxPricedList(all *map[common.Hash]*types.Transaction) *txPricedList {
return &txPricedList{
all: all,
items: new(priceHeap),
}
}
// Put inserts a new transaction into the heap.
func (l *txPricedList) Put(tx *types.Transaction) {
heap.Push(l.items, tx)
}
// Removed notifies the prices transaction list that an old transaction dropped
// from the pool. The list will just keep a counter of stale objects and update
// the heap if a large enough ratio of transactions go stale.
func (l *txPricedList) Removed() {
// Bump the stale counter, but exit if still too low (< 25%)
l.stales++
if l.stales <= len(*l.items)/4 {
return
}
// Seems we've reached a critical number of stale transactions, reheap
reheap := make(priceHeap, 0, len(*l.all))
l.stales, l.items = 0, &reheap
for _, tx := range *l.all {
*l.items = append(*l.items, tx)
}
heap.Init(l.items)
}
// Discard finds all the transactions below the given price threshold, drops them
// from the priced list and returs them for further removal from the entire pool.
func (l *txPricedList) Cap(threshold *big.Int, local *txSet) types.Transactions {
drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
for len(*l.items) > 0 {
// Discard stale transactions if found during cleanup
tx := heap.Pop(l.items).(*types.Transaction)
hash := tx.Hash()
if _, ok := (*l.all)[hash]; !ok {
l.stales--
continue
}
// Stop the discards if we've reached the threshold
if tx.GasPrice().Cmp(threshold) >= 0 {
break
}
// Non stale transaction found, discard unless local
if local.contains(hash) {
save = append(save, tx)
} else {
drop = append(drop, tx)
}
}
for _, tx := range save {
heap.Push(l.items, tx)
}
return drop
}
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced transaction currently being tracked.
func (l *txPricedList) Underpriced(tx *types.Transaction, local *txSet) bool {
// Local transactions cannot be underpriced
if local.contains(tx.Hash()) {
return false
}
// Discard stale price points if found at the heap start
for len(*l.items) > 0 {
head := []*types.Transaction(*l.items)[0]
if _, ok := (*l.all)[head.Hash()]; !ok {
l.stales--
heap.Pop(l.items)
continue
}
break
}
// Check if the transaction is underpriced or not
if len(*l.items) == 0 {
log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
return false
}
cheapest := []*types.Transaction(*l.items)[0]
return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0
}
// Discard finds a number of most underpriced transactions, removes them from the
// priced list and returs them for further removal from the entire pool.
func (l *txPricedList) Discard(count int, local *txSet) types.Transactions {
drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
for len(*l.items) > 0 && count > 0 {
// Discard stale transactions if found during cleanup
tx := heap.Pop(l.items).(*types.Transaction)
hash := tx.Hash()
if _, ok := (*l.all)[hash]; !ok {
l.stales--
continue
}
// Non stale transaction found, discard unless local
if local.contains(hash) {
save = append(save, tx)
} else {
drop = append(drop, tx)
count--
}
}
for _, tx := range save {
heap.Push(l.items, tx)
}
return drop
}
This diff is collapsed.
This diff is collapsed.
...@@ -153,6 +153,8 @@ func (api *PrivateMinerAPI) Start(threads *int) error { ...@@ -153,6 +153,8 @@ func (api *PrivateMinerAPI) Start(threads *int) error {
} }
// Start the miner and return // Start the miner and return
if !api.e.IsMining() { if !api.e.IsMining() {
// Propagate the initial price point to the transaction pool
api.e.txPool.SetGasPrice(api.e.gasPrice)
return api.e.StartMining(true) return api.e.StartMining(true)
} }
return nil return nil
...@@ -180,7 +182,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) { ...@@ -180,7 +182,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
// SetGasPrice sets the minimum accepted gas price for the miner. // SetGasPrice sets the minimum accepted gas price for the miner.
func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
api.e.Miner().SetGasPrice((*big.Int)(&gasPrice)) api.e.txPool.SetGasPrice((*big.Int)(&gasPrice))
return true return true
} }
......
...@@ -20,6 +20,7 @@ package eth ...@@ -20,6 +20,7 @@ package eth
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"runtime" "runtime"
"sync" "sync"
"sync/atomic" "sync/atomic"
...@@ -76,6 +77,7 @@ type Ethereum struct { ...@@ -76,6 +77,7 @@ type Ethereum struct {
ApiBackend *EthApiBackend ApiBackend *EthApiBackend
miner *miner.Miner miner *miner.Miner
gasPrice *big.Int
Mining bool Mining bool
MinerThreads int MinerThreads int
etherbase common.Address etherbase common.Address
...@@ -167,7 +169,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { ...@@ -167,7 +169,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
} }
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
eth.miner.SetGasPrice(config.GasPrice) eth.gasPrice = config.GasPrice
eth.miner.SetExtra(makeExtraData(config.ExtraData)) eth.miner.SetExtra(makeExtraData(config.ExtraData))
eth.ApiBackend = &EthApiBackend{eth, nil} eth.ApiBackend = &EthApiBackend{eth, nil}
......
...@@ -42,7 +42,7 @@ var DefaultConfig = Config{ ...@@ -42,7 +42,7 @@ var DefaultConfig = Config{
NetworkId: 1, NetworkId: 1,
LightPeers: 20, LightPeers: 20,
DatabaseCache: 128, DatabaseCache: 128,
GasPrice: big.NewInt(20 * params.Shannon), GasPrice: big.NewInt(18 * params.Shannon),
GPO: gasprice.Config{ GPO: gasprice.Config{
Blocks: 10, Blocks: 10,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package ethstats package ethstats
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
...@@ -639,7 +640,8 @@ func (s *Service) reportStats(conn *websocket.Conn) error { ...@@ -639,7 +640,8 @@ func (s *Service) reportStats(conn *websocket.Conn) error {
sync := s.eth.Downloader().Progress() sync := s.eth.Downloader().Progress()
syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
gasprice = int(s.eth.Miner().GasPrice().Uint64()) price, _ := s.eth.ApiBackend.SuggestPrice(context.Background())
gasprice = int(price.Uint64())
} else { } else {
sync := s.les.Downloader().Progress() sync := s.les.Downloader().Progress()
syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
......
...@@ -19,7 +19,6 @@ package miner ...@@ -19,7 +19,6 @@ package miner
import ( import (
"fmt" "fmt"
"math/big"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
...@@ -104,18 +103,6 @@ out: ...@@ -104,18 +103,6 @@ out:
} }
} }
func (m *Miner) GasPrice() *big.Int {
return new(big.Int).Set(m.worker.gasPrice)
}
func (m *Miner) SetGasPrice(price *big.Int) {
// FIXME block tests set a nil gas price. Quick dirty fix
if price == nil {
return
}
m.worker.setGasPrice(price)
}
func (self *Miner) Start(coinbase common.Address) { func (self *Miner) Start(coinbase common.Address) {
atomic.StoreInt32(&self.shouldStart, 1) atomic.StoreInt32(&self.shouldStart, 1)
self.worker.setEtherbase(coinbase) self.worker.setEtherbase(coinbase)
......
...@@ -64,8 +64,6 @@ type Work struct { ...@@ -64,8 +64,6 @@ type Work struct {
family *set.Set // family set (used for checking uncle invalidity) family *set.Set // family set (used for checking uncle invalidity)
uncles *set.Set // uncle set uncles *set.Set // uncle set
tcount int // tx count in cycle tcount int // tx count in cycle
ownedAccounts *set.Set
lowGasTxs types.Transactions
failedTxs types.Transactions failedTxs types.Transactions
Block *types.Block // the new block Block *types.Block // the new block
...@@ -103,7 +101,6 @@ type worker struct { ...@@ -103,7 +101,6 @@ type worker struct {
chainDb ethdb.Database chainDb ethdb.Database
coinbase common.Address coinbase common.Address
gasPrice *big.Int
extra []byte extra []byte
currentMu sync.Mutex currentMu sync.Mutex
...@@ -132,7 +129,6 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com ...@@ -132,7 +129,6 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
mux: mux, mux: mux,
chainDb: eth.ChainDb(), chainDb: eth.ChainDb(),
recv: make(chan *Result, resultQueueSize), recv: make(chan *Result, resultQueueSize),
gasPrice: new(big.Int),
chain: eth.BlockChain(), chain: eth.BlockChain(),
proc: eth.BlockChain().Validator(), proc: eth.BlockChain().Validator(),
possibleUncles: make(map[common.Hash]*types.Block), possibleUncles: make(map[common.Hash]*types.Block),
...@@ -252,7 +248,7 @@ func (self *worker) update() { ...@@ -252,7 +248,7 @@ func (self *worker) update() {
txs := map[common.Address]types.Transactions{acc: {ev.Tx}} txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
txset := types.NewTransactionsByPriceAndNonce(txs) txset := types.NewTransactionsByPriceAndNonce(txs)
self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain, self.coinbase) self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
self.currentMu.Unlock() self.currentMu.Unlock()
} }
} }
...@@ -375,22 +371,10 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error ...@@ -375,22 +371,10 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
} }
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
work.tcount = 0 work.tcount = 0
work.ownedAccounts = accountAddressesSet(accounts)
self.current = work self.current = work
return nil return nil
} }
func (w *worker) setGasPrice(p *big.Int) {
w.mu.Lock()
defer w.mu.Unlock()
// calculate the minimal gas price the miner accepts when sorting out transactions.
const pct = int64(90)
w.gasPrice = gasprice(p, pct)
w.mux.Post(core.GasPriceChanged{Price: w.gasPrice})
}
func (self *worker) commitNewWork() { func (self *worker) commitNewWork() {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
...@@ -460,9 +444,8 @@ func (self *worker) commitNewWork() { ...@@ -460,9 +444,8 @@ func (self *worker) commitNewWork() {
return return
} }
txs := types.NewTransactionsByPriceAndNonce(pending) txs := types.NewTransactionsByPriceAndNonce(pending)
work.commitTransactions(self.mux, txs, self.gasPrice, self.chain, self.coinbase) work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
self.eth.TxPool().RemoveBatch(work.lowGasTxs)
self.eth.TxPool().RemoveBatch(work.failedTxs) self.eth.TxPool().RemoveBatch(work.failedTxs)
// compute uncles for the new block. // compute uncles for the new block.
...@@ -515,7 +498,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { ...@@ -515,7 +498,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
return nil return nil
} }
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain, coinbase common.Address) { func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
gp := new(core.GasPool).AddGas(env.header.GasLimit) gp := new(core.GasPool).AddGas(env.header.GasLimit)
var coalescedLogs []*types.Log var coalescedLogs []*types.Log
...@@ -539,17 +522,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB ...@@ -539,17 +522,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
txs.Pop() txs.Pop()
continue continue
} }
// Ignore any transactions (and accounts subsequently) with low gas limits
if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) {
// Pop the current low-priced transaction without shifting in the next from the account
log.Warn("Transaction below gas price", "sender", from, "hash", tx.Hash(), "have", tx.GasPrice(), "want", gasPrice)
env.lowGasTxs = append(env.lowGasTxs, tx)
txs.Pop()
continue
}
// Start executing the transaction // Start executing the transaction
env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount)
...@@ -607,25 +579,3 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, c ...@@ -607,25 +579,3 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, c
return nil, receipt.Logs return nil, receipt.Logs
} }
// TODO: remove or use
func (self *worker) HashRate() int64 {
return 0
}
// gasprice calculates a reduced gas price based on the pct
// XXX Use big.Rat?
func gasprice(price *big.Int, pct int64) *big.Int {
p := new(big.Int).Set(price)
p.Div(p, big.NewInt(100))
p.Mul(p, big.NewInt(pct))
return p
}
func accountAddressesSet(accounts []accounts.Account) *set.Set {
accountSet := set.New()
for _, account := range accounts {
accountSet.Add(account.Address)
}
return accountSet
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment