From b63c37dbdd0db276072bd3f45d1e20a1122b6a12 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 31 May 2021 18:44:07 +0800 Subject: [PATCH 1/2] core, eth, internal, les: simplify gasprice oracle --- core/chain_makers.go | 10 +-- eth/api_backend.go | 6 +- eth/gasprice/gasprice.go | 103 +++++++++++++--------------- eth/gasprice/gasprice_test.go | 64 ++++++++--------- eth/protocols/snap/range_test.go | 2 +- internal/ethapi/backend.go | 1 - internal/ethapi/transaction_args.go | 2 +- les/api_backend.go | 6 +- 8 files changed, 84 insertions(+), 110 deletions(-) diff --git a/core/chain_makers.go b/core/chain_makers.go index f7353ffce3f8..deeba1c76e99 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -252,7 +252,6 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S } else { time = parent.Time() + 10 // block time is fixed at 10 seconds } - header := &types.Header{ Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), ParentHash: parent.Hash(), @@ -267,11 +266,14 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } - - if chain.Config().IsLondon(parent.Number()) { + if chain.Config().IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) + parentGasLimit := parent.GasLimit() + if !chain.Config().IsLondon(parent.Number()) { + parentGasLimit = parent.GasLimit() * params.ElasticityMultiplier + } + header.GasLimit = CalcGasLimit1559(parentGasLimit, parentGasLimit) } - return header } diff --git a/eth/api_backend.go b/eth/api_backend.go index 0a87798c43b6..08148f3ca3d4 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -276,11 +276,7 @@ func (b *EthAPIBackend) Downloader() *downloader.Downloader { } func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx, false) -} - -func (b *EthAPIBackend) SuggestTip(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx, true) + return b.gpo.SuggestPrice(ctx) } func (b *EthAPIBackend) SuggestFeeCap(ctx context.Context) (*big.Int, error) { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index fb1207f2033e..7f912199381e 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -18,6 +18,7 @@ package gasprice import ( "context" + "fmt" "math/big" "sort" "sync" @@ -29,10 +30,12 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -const sampleNumber = 3 // Number of transactions sampled in a block +const sampleNumber = 3 // Number of txs sampled in a block -var DefaultMaxPrice = big.NewInt(500 * params.GWei) -var DefaultIgnorePrice = big.NewInt(2 * params.Wei) +var ( + DefaultMaxPrice = big.NewInt(500 * params.GWei) + DefaultIgnorePrice = big.NewInt(2 * params.Wei) +) type Config struct { Blocks int @@ -105,7 +108,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { // SuggestPrice returns a gasprice or tip so that newly created transaction // can have a very high chance to be included in the following blocks. -func (gpo *Oracle) SuggestPrice(ctx context.Context, tip bool) (*big.Int, error) { +func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) headHash := head.Hash() @@ -131,10 +134,10 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context, tip bool) (*big.Int, error) number = head.Number.Uint64() result = make(chan results, gpo.checkBlocks) quit = make(chan struct{}) - txPrices []*big.Int + results []*big.Int ) for sent < gpo.checkBlocks && number > 0 { - go gpo.getBlockValues(ctx, tip, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) sent++ exp++ number-- @@ -148,7 +151,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context, tip bool) (*big.Int, error) exp-- // Nothing returned. There are two special cases here: // - The block is empty - // - All the transactions included are sent by the miner itself. + // - All the txs included are sent by the miner itself. // In these cases, use the latest calculated price for samping. if len(res.values) == 0 { res.values = []*big.Int{lastPrice} @@ -156,18 +159,19 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context, tip bool) (*big.Int, error) // Besides, in order to collect enough data for sampling, if nothing // meaningful returned, try to query more blocks. But the maximum // is 2*checkBlocks. - if len(res.values) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 { - go gpo.getBlockValues(ctx, tip, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) + if len(res.values) == 1 && len(results)+1+exp < gpo.checkBlocks*2 && number > 0 { + go gpo.getBlockValues(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit) sent++ exp++ number-- } - txPrices = append(txPrices, res.values...) + results = append(results, res.values...) } + fmt.Println(results) price := lastPrice - if len(txPrices) > 0 { - sort.Sort(bigIntArray(txPrices)) - price = txPrices[(len(txPrices)-1)*gpo.percentile/100] + if len(results) > 0 { + sort.Sort(bigIntArray(results)) + price = results[(len(results)-1)*gpo.percentile/100] } if price.Cmp(gpo.maxPrice) > 0 { price = new(big.Int).Set(gpo.maxPrice) @@ -184,40 +188,35 @@ type results struct { err error } -type SortableTxs interface { - Len() int - Less(i, j int) bool - Swap(i, j int) - GasPrice(int) *big.Int - EffectiveTip(int, *big.Int) *big.Int +type txSorter struct { + txs []*types.Transaction + baseFee *big.Int } -type transactionsByFeeCap []*types.Transaction - -func (t transactionsByFeeCap) Len() int { return len(t) } -func (t transactionsByFeeCap) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t transactionsByFeeCap) Less(i, j int) bool { return t[i].FeeCapCmp(t[j]) < 0 } -func (t transactionsByFeeCap) GasPrice(i int) *big.Int { return t[i].GasPrice() } -func (t transactionsByFeeCap) EffectiveTip(i int, b *big.Int) *big.Int { return nil } - -type transactionsByTip []*types.Transaction +func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter { + return &txSorter{ + txs: txs, + baseFee: baseFee, + } +} -func (t transactionsByTip) Len() int { return len(t) } -func (t transactionsByTip) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t transactionsByTip) Less(i, j int) bool { return t[i].TipCmp(t[j]) < 0 } -func (t transactionsByTip) GasPrice(i int) *big.Int { return nil } -func (t transactionsByTip) EffectiveTip(i int, b *big.Int) *big.Int { - // It's okay to discard the error because a tx would never be accepted into - // a block with an invalid effective tip. - et, _ := t[i].EffectiveTip(b) - return et +func (s *txSorter) Len() int { return len(s.txs) } +func (s *txSorter) Swap(i, j int) { + s.txs[i], s.txs[j] = s.txs[j], s.txs[i] +} +func (s *txSorter) Less(i, j int) bool { + // It's okay to discard the error because a tx would never be + // accepted into a block with an invalid effective tip. + tip1, _ := s.txs[i].EffectiveTip(s.baseFee) + tip2, _ := s.txs[j].EffectiveTip(s.baseFee) + return tip1.Cmp(tip2) < 0 } // getBlockPrices calculates the lowest transaction gas price in a given block -// and sends it to the result channel. If the block is empty or all transactions +// and sends it to the result channel. If the block is empty or all txs // are sent by the miner itself(it doesn't make any sense to include this kind of // transaction prices for sampling), nil gasprice is returned. -func (gpo *Oracle) getBlockValues(ctx context.Context, tip bool, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { +func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) { block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if block == nil { select { @@ -226,29 +225,21 @@ func (gpo *Oracle) getBlockValues(ctx context.Context, tip bool, signer types.Si } return } - blockTxs := block.Transactions() - txs := make([]*types.Transaction, len(blockTxs)) - copy(txs, blockTxs) - - if tip { - sort.Sort(transactionsByTip(txs)) - } else { - sort.Sort(transactionsByFeeCap(txs)) - } + // Sort the transaction by effective tip in ascending sort. + txs := make([]*types.Transaction, len(block.Transactions())) + copy(txs, block.Transactions()) + sorter := newSorter(txs, block.BaseFee()) + sort.Sort(sorter) var prices []*big.Int - for _, tx := range txs { - if ignoreUnder != nil && tx.TipIntCmp(ignoreUnder) == -1 { + for _, tx := range sorter.txs { + tip, _ := tx.EffectiveTip(block.BaseFee()) + if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 { continue } sender, err := types.Sender(signer, tx) if err == nil && sender != block.Coinbase() { - if tip { - et, _ := tx.EffectiveTip(block.BaseFee()) - prices = append(prices, et) - } else { - prices = append(prices, tx.GasPrice()) - } + prices = append(prices, tip) if len(prices) >= limit { break } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index d22a89d3bc84..33b7484b1780 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -55,7 +55,7 @@ func (b *testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() } -func newTestBackend(t *testing.T, isEIP1559 bool) *testBackend { +func newTestBackend(t *testing.T, londonBlock *big.Int) *testBackend { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key.PublicKey) @@ -65,8 +65,8 @@ func newTestBackend(t *testing.T, isEIP1559 bool) *testBackend { } signer = types.LatestSigner(gspec.Config) ) - if isEIP1559 { - gspec.Config.LondonBlock = common.Big0 + if londonBlock != nil { + gspec.Config.LondonBlock = londonBlock signer = types.LatestSigner(gspec.Config) } engine := ethash.NewFaker() @@ -74,18 +74,18 @@ func newTestBackend(t *testing.T, isEIP1559 bool) *testBackend { genesis, _ := gspec.Commit(db) // Generate testing blocks - blocks, _ := core.GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *core.BlockGen) { + blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 32, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) var tx *types.Transaction - if isEIP1559 { + if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { txdata := &types.DynamicFeeTx{ ChainID: gspec.Config.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, Gas: 30000, FeeCap: big.NewInt(100 * params.GWei), - Tip: big.NewInt(int64(i+1) * params.GWei * 2), + Tip: big.NewInt(int64(i+1) * params.GWei), Data: []byte{}, } tx = types.NewTx(txdata) @@ -100,7 +100,6 @@ func newTestBackend(t *testing.T, isEIP1559 bool) *testBackend { } tx = types.NewTx(txdata) } - tx, err := types.SignTx(tx, signer, key) if err != nil { t.Fatalf("failed to create tx: %v", err) @@ -110,7 +109,7 @@ func newTestBackend(t *testing.T, isEIP1559 bool) *testBackend { // Construct testing chain diskdb := rawdb.NewMemoryDatabase() gspec.Commit(diskdb) - chain, err := core.NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to create local chain, %v", err) } @@ -132,36 +131,27 @@ func TestSuggestPrice(t *testing.T) { Percentile: 60, Default: big.NewInt(params.GWei), } - backend := newTestBackend(t, false) - oracle := NewOracle(backend, config) - - // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G - got, err := oracle.SuggestPrice(context.Background(), false) - if err != nil { - t.Fatalf("Failed to retrieve recommended gas price: %v", err) - } - expect := big.NewInt(params.GWei * int64(30)) - if got.Cmp(expect) != 0 { - t.Fatalf("Gas price mismatch, want %d, got %d", expect, got) - } -} - -func TestSuggestTip(t *testing.T) { - config := Config{ - Blocks: 3, - Percentile: 60, - Default: big.NewInt(params.GWei), + var cases = []struct { + fork *big.Int // London fork number + expect *big.Int // Expected gasprice suggestion + }{ + {nil, big.NewInt(params.GWei * int64(30))}, + {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis + {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block + {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block + {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future } - backend := newTestBackend(t, true) - oracle := NewOracle(backend, config) + for _, c := range cases { + backend := newTestBackend(t, c.fork) + oracle := NewOracle(backend, config) - // The gas price sampled is: 62G, 61G, 60G, 59G, 58G, 57G - got, err := oracle.SuggestPrice(context.Background(), false) - if err != nil { - t.Fatalf("Failed to retrieve recommended gas price: %v", err) - } - expect := big.NewInt(params.GWei * int64(60)) - if got.Cmp(expect) != 0 { - t.Fatalf("Gas price mismatch, want %d, got %d", expect, got) + // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G + got, err := oracle.SuggestPrice(context.Background()) + if err != nil { + t.Fatalf("Failed to retrieve recommended gas price: %v", err) + } + if got.Cmp(c.expect) != 0 { + t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got) + } } } diff --git a/eth/protocols/snap/range_test.go b/eth/protocols/snap/range_test.go index 23273e50bf10..c6dc8fb718ae 100644 --- a/eth/protocols/snap/range_test.go +++ b/eth/protocols/snap/range_test.go @@ -53,7 +53,7 @@ func TestHashRanges(t *testing.T) { head: common.HexToHash("0x2000000000000000000000000000000000000000000000000000000000000000"), chunks: 2, starts: []common.Hash{ - common.Hash{}, + {}, common.HexToHash("0x9000000000000000000000000000000000000000000000000000000000000000"), }, ends: []common.Hash{ diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index fee45a180d81..f63c116a3073 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -42,7 +42,6 @@ type Backend interface { // General Ethereum API Downloader() *downloader.Downloader SuggestPrice(ctx context.Context) (*big.Int, error) - SuggestTip(ctx context.Context) (*big.Int, error) SuggestFeeCap(ctx context.Context) (*big.Int, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 942c82f41869..d09f031e2adb 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -80,7 +80,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { // After london, default to 1559 unless gasPrice is set if b.ChainConfig().IsLondon(b.CurrentBlock().Number()) && args.GasPrice == nil { if args.Tip == nil { - tip, err := b.SuggestTip(ctx) + tip, err := b.SuggestPrice(ctx) if err != nil { return err } diff --git a/les/api_backend.go b/les/api_backend.go index 6c1e53ff677d..60a3ce68b005 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -252,11 +252,7 @@ func (b *LesApiBackend) ProtocolVersion() int { } func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx, false) -} - -func (b *LesApiBackend) SuggestTip(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx, true) + return b.gpo.SuggestPrice(ctx) } func (b *LesApiBackend) SuggestFeeCap(ctx context.Context) (*big.Int, error) { From 1dcc37b7169b076493ade7094b8f93070ac32b8e Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 31 May 2021 18:59:43 +0800 Subject: [PATCH 2/2] eth/gasprice: fix typo --- eth/gasprice/gasprice.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 7f912199381e..9a414754aa96 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) -const sampleNumber = 3 // Number of txs sampled in a block +const sampleNumber = 3 // Number of transactions sampled in a block var ( DefaultMaxPrice = big.NewInt(500 * params.GWei) @@ -151,7 +151,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { exp-- // Nothing returned. There are two special cases here: // - The block is empty - // - All the txs included are sent by the miner itself. + // - All the transactions included are sent by the miner itself. // In these cases, use the latest calculated price for samping. if len(res.values) == 0 { res.values = []*big.Int{lastPrice} @@ -213,7 +213,7 @@ func (s *txSorter) Less(i, j int) bool { } // getBlockPrices calculates the lowest transaction gas price in a given block -// and sends it to the result channel. If the block is empty or all txs +// and sends it to the result channel. If the block is empty or all transactions // are sent by the miner itself(it doesn't make any sense to include this kind of // transaction prices for sampling), nil gasprice is returned. func (gpo *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {