From b06e20bc7c498adef658b58f10f5c729b46d84f9 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 10 Jan 2018 12:57:36 +0000 Subject: [PATCH] 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. --- eth/config.go | 4 ++-- eth/gasprice/gasprice.go | 49 ++++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/eth/config.go b/eth/config.go index 383cd6783c..4399560fa3 100644 --- a/eth/config.go +++ b/eth/config.go @@ -49,8 +49,8 @@ var DefaultConfig = Config{ TxPool: core.DefaultTxPoolConfig, GPO: gasprice.Config{ - Blocks: 10, - Percentile: 50, + Blocks: 20, + Percentile: 60, }, } diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index c662348e16..54325692c6 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -23,6 +23,7 @@ import ( "sync" "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/params" "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) sent := 0 exp := 0 - var txPrices []*big.Int + var blockPrices []*big.Int 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++ exp++ blockNum-- @@ -115,8 +116,8 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { return lastPrice, res.err } exp-- - if len(res.prices) > 0 { - txPrices = append(txPrices, res.prices...) + if res.price != nil { + blockPrices = append(blockPrices, res.price) continue } if maxEmpty > 0 { @@ -124,16 +125,16 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { continue } 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++ exp++ blockNum-- } } price := lastPrice - if len(txPrices) > 0 { - sort.Sort(bigIntArray(txPrices)) - price = txPrices[(len(txPrices)-1)*gpo.percentile/100] + if len(blockPrices) > 0 { + sort.Sort(bigIntArray(blockPrices)) + price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100] } if price.Cmp(maxPrice) > 0 { price = new(big.Int).Set(maxPrice) @@ -147,24 +148,38 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { } type getBlockPricesResult struct { - prices []*big.Int - err error + price *big.Int + 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. -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)) if block == nil { ch <- getBlockPricesResult{nil, err} return } - txs := block.Transactions() - prices := make([]*big.Int, len(txs)) - for i, tx := range txs { - prices[i] = tx.GasPrice() + + blockTxs := block.Transactions() + txs := make([]*types.Transaction, len(blockTxs)) + 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