github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/gasprice/fee_info_provider.go (about) 1 // (c) 2019-2022, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2015 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package gasprice 28 29 import ( 30 "context" 31 "math/big" 32 33 "github.com/MetalBlockchain/subnet-evm/core" 34 "github.com/MetalBlockchain/subnet-evm/core/types" 35 "github.com/MetalBlockchain/subnet-evm/rpc" 36 lru "github.com/hashicorp/golang-lru" 37 ) 38 39 // additional slots in the header cache to allow processing queries 40 // for previous blocks (with full number of blocks desired) if new 41 // blocks are added concurrently. 42 const feeCacheExtraSlots = 5 43 44 type feeInfoProvider struct { 45 cache *lru.Cache 46 backend OracleBackend 47 // [minGasUsed] ensures we don't recommend users pay non-zero tips when other 48 // users are paying a tip to unnecessarily expedite block production. 49 minGasUsed uint64 50 newHeaderAdded func() // callback used in tests 51 } 52 53 // feeInfo is the type of data stored in feeInfoProvider's cache. 54 type feeInfo struct { 55 baseFee, tip *big.Int // baseFee and min. suggested tip for tx to be included in the block 56 timestamp uint64 // timestamp of the block header 57 } 58 59 // newFeeInfoProvider returns a bounded buffer with [size] slots to 60 // store [*feeInfo] for the most recently accepted blocks. 61 func newFeeInfoProvider(backend OracleBackend, minGasUsed uint64, size int) (*feeInfoProvider, error) { 62 fc := &feeInfoProvider{ 63 backend: backend, 64 minGasUsed: minGasUsed, 65 } 66 if size == 0 { 67 // if size is zero, we return early as there is no 68 // reason for a goroutine to subscribe to the chain's 69 // accepted event. 70 fc.cache, _ = lru.New(size) 71 return fc, nil 72 } 73 74 fc.cache, _ = lru.New(size + feeCacheExtraSlots) 75 // subscribe to the chain accepted event 76 acceptedEvent := make(chan core.ChainEvent, 1) 77 backend.SubscribeChainAcceptedEvent(acceptedEvent) 78 go func() { 79 for ev := range acceptedEvent { 80 fc.addHeader(context.Background(), ev.Block.Header()) 81 if fc.newHeaderAdded != nil { 82 fc.newHeaderAdded() 83 } 84 } 85 }() 86 return fc, fc.populateCache(size) 87 } 88 89 // addHeader processes header into a feeInfo struct and caches the result. 90 func (f *feeInfoProvider) addHeader(ctx context.Context, header *types.Header) (*feeInfo, error) { 91 feeInfo := &feeInfo{ 92 timestamp: header.Time, 93 baseFee: header.BaseFee, 94 } 95 // Don't bias the estimate with blocks containing a limited number of transactions paying to 96 // expedite block production. 97 var err error 98 if f.minGasUsed <= header.GasUsed { 99 // Compute minimum required tip to be included in previous block 100 // 101 // NOTE: Using this approach, we will never recommend that the caller 102 // provides a non-zero tip unless some block is produced faster than the 103 // target rate (which could only occur if some set of callers manually override the 104 // suggested tip). In the future, we may wish to start suggesting a non-zero 105 // tip when most blocks are full otherwise callers may observe an unexpected 106 // delay in transaction inclusion. 107 feeInfo.tip, err = f.backend.MinRequiredTip(ctx, header) 108 } 109 110 f.cache.Add(header.Number.Uint64(), feeInfo) 111 return feeInfo, err 112 } 113 114 // get returns the feeInfo for block with [number] if present in the cache 115 // and a boolean representing if it was found. 116 func (f *feeInfoProvider) get(number uint64) (*feeInfo, bool) { 117 // Note: use Peek on LRU to use it as a bounded buffer. 118 feeInfoIntf, ok := f.cache.Peek(number) 119 if ok { 120 return feeInfoIntf.(*feeInfo), ok 121 } 122 return nil, ok 123 } 124 125 // populateCache populates [f] with [size] blocks up to last accepted. 126 // Note: assumes [size] is greater than zero. 127 func (f *feeInfoProvider) populateCache(size int) error { 128 lastAccepted := f.backend.LastAcceptedBlock().NumberU64() 129 lowerBlockNumber := uint64(0) 130 if uint64(size-1) <= lastAccepted { // Note: "size-1" because we need a total of size blocks. 131 lowerBlockNumber = lastAccepted - uint64(size-1) 132 } 133 134 for i := lowerBlockNumber; i <= lastAccepted; i++ { 135 header, err := f.backend.HeaderByNumber(context.Background(), rpc.BlockNumber(i)) 136 if err != nil { 137 return err 138 } 139 _, err = f.addHeader(context.Background(), header) 140 if err != nil { 141 return err 142 } 143 } 144 return nil 145 }