eth/gasprice: set default percentile to 60%, count blocks instead of transactions (#15828)

The first should address a long term issue where we recommend a gas
price that is greater than that required for 50% of transactions in
recent blocks, which can lead to gas price inflation as people take
this figure and add a margin to it, resulting in a positive feedback
loop.
This commit is contained in:
Nick Johnson 2018-01-10 12:57:36 +00:00 committed by Felix Lange
parent 3a5a5599dd
commit b06e20bc7c
2 changed files with 34 additions and 19 deletions

View File

@ -49,8 +49,8 @@ var DefaultConfig = Config{
TxPool: core.DefaultTxPoolConfig, TxPool: core.DefaultTxPoolConfig,
GPO: gasprice.Config{ GPO: gasprice.Config{
Blocks: 10, Blocks: 20,
Percentile: 50, Percentile: 60,
}, },
} }

View File

@ -23,6 +23,7 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -101,9 +102,9 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
ch := make(chan getBlockPricesResult, gpo.checkBlocks) ch := make(chan getBlockPricesResult, gpo.checkBlocks)
sent := 0 sent := 0
exp := 0 exp := 0
var txPrices []*big.Int var blockPrices []*big.Int
for sent < gpo.checkBlocks && blockNum > 0 { for sent < gpo.checkBlocks && blockNum > 0 {
go gpo.getBlockPrices(ctx, blockNum, ch) go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
sent++ sent++
exp++ exp++
blockNum-- blockNum--
@ -115,8 +116,8 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
return lastPrice, res.err return lastPrice, res.err
} }
exp-- exp--
if len(res.prices) > 0 { if res.price != nil {
txPrices = append(txPrices, res.prices...) blockPrices = append(blockPrices, res.price)
continue continue
} }
if maxEmpty > 0 { if maxEmpty > 0 {
@ -124,16 +125,16 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
continue continue
} }
if blockNum > 0 && sent < gpo.maxBlocks { if blockNum > 0 && sent < gpo.maxBlocks {
go gpo.getBlockPrices(ctx, blockNum, ch) go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
sent++ sent++
exp++ exp++
blockNum-- blockNum--
} }
} }
price := lastPrice price := lastPrice
if len(txPrices) > 0 { if len(blockPrices) > 0 {
sort.Sort(bigIntArray(txPrices)) sort.Sort(bigIntArray(blockPrices))
price = txPrices[(len(txPrices)-1)*gpo.percentile/100] price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
} }
if price.Cmp(maxPrice) > 0 { if price.Cmp(maxPrice) > 0 {
price = new(big.Int).Set(maxPrice) price = new(big.Int).Set(maxPrice)
@ -147,24 +148,38 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
} }
type getBlockPricesResult struct { type getBlockPricesResult struct {
prices []*big.Int price *big.Int
err error err error
} }
// getLowestPrice calculates the lowest transaction gas price in a given block type transactionsByGasPrice []*types.Transaction
func (t transactionsByGasPrice) Len() int { return len(t) }
func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 }
// getBlockPrices calculates the lowest transaction gas price in a given block
// and sends it to the result channel. If the block is empty, price is nil. // and sends it to the result channel. If the block is empty, price is nil.
func (gpo *Oracle) getBlockPrices(ctx context.Context, blockNum uint64, ch chan getBlockPricesResult) { func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) {
block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
if block == nil { if block == nil {
ch <- getBlockPricesResult{nil, err} ch <- getBlockPricesResult{nil, err}
return return
} }
txs := block.Transactions()
prices := make([]*big.Int, len(txs)) blockTxs := block.Transactions()
for i, tx := range txs { txs := make([]*types.Transaction, len(blockTxs))
prices[i] = tx.GasPrice() copy(txs, blockTxs)
sort.Sort(transactionsByGasPrice(txs))
for _, tx := range txs {
sender, err := types.Sender(signer, tx)
if err == nil && sender != block.Coinbase() {
ch <- getBlockPricesResult{tx.GasPrice(), nil}
return
}
} }
ch <- getBlockPricesResult{prices, nil} ch <- getBlockPricesResult{nil, nil}
} }
type bigIntArray []*big.Int type bigIntArray []*big.Int