github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/neatptc/gasprice/gasprice.go (about) 1 package gasprice 2 3 import ( 4 "context" 5 "math/big" 6 "sort" 7 "sync" 8 9 "github.com/neatlab/neatio/chain/core/types" 10 "github.com/neatlab/neatio/internal/neatapi" 11 "github.com/neatlab/neatio/network/rpc" 12 "github.com/neatlab/neatio/params" 13 "github.com/neatlab/neatio/utilities/common" 14 ) 15 16 var maxPrice = big.NewInt(5000 * params.GWei) 17 18 type Config struct { 19 Blocks int 20 Percentile int 21 Default *big.Int `toml:",omitempty"` 22 } 23 24 type Oracle struct { 25 backend neatapi.Backend 26 lastHead common.Hash 27 lastPrice *big.Int 28 cacheLock sync.RWMutex 29 fetchLock sync.Mutex 30 31 checkBlocks, maxEmpty, maxBlocks int 32 percentile int 33 } 34 35 func NewOracle(backend neatapi.Backend, params Config) *Oracle { 36 blocks := params.Blocks 37 if blocks < 1 { 38 blocks = 1 39 } 40 percent := params.Percentile 41 if percent < 0 { 42 percent = 0 43 } 44 if percent > 100 { 45 percent = 100 46 } 47 return &Oracle{ 48 backend: backend, 49 lastPrice: params.Default, 50 checkBlocks: blocks, 51 maxEmpty: blocks / 2, 52 maxBlocks: blocks * 5, 53 percentile: percent, 54 } 55 } 56 57 func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { 58 gpo.cacheLock.RLock() 59 lastHead := gpo.lastHead 60 lastPrice := gpo.lastPrice 61 gpo.cacheLock.RUnlock() 62 63 head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 64 headHash := head.Hash() 65 if headHash == lastHead { 66 return lastPrice, nil 67 } 68 69 gpo.fetchLock.Lock() 70 defer gpo.fetchLock.Unlock() 71 72 gpo.cacheLock.RLock() 73 lastHead = gpo.lastHead 74 lastPrice = gpo.lastPrice 75 gpo.cacheLock.RUnlock() 76 if headHash == lastHead { 77 return lastPrice, nil 78 } 79 80 blockNum := head.Number.Uint64() 81 ch := make(chan getBlockPricesResult, gpo.checkBlocks) 82 sent := 0 83 exp := 0 84 var blockPrices []*big.Int 85 for sent < gpo.checkBlocks && blockNum > 0 { 86 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 87 sent++ 88 exp++ 89 blockNum-- 90 } 91 maxEmpty := gpo.maxEmpty 92 for exp > 0 { 93 res := <-ch 94 if res.err != nil { 95 return lastPrice, res.err 96 } 97 exp-- 98 if res.price != nil { 99 blockPrices = append(blockPrices, res.price) 100 continue 101 } 102 if maxEmpty > 0 { 103 maxEmpty-- 104 continue 105 } 106 if blockNum > 0 && sent < gpo.maxBlocks { 107 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 108 sent++ 109 exp++ 110 blockNum-- 111 } 112 } 113 price := lastPrice 114 if len(blockPrices) > 0 { 115 sort.Sort(bigIntArray(blockPrices)) 116 price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100] 117 } 118 if price.Cmp(maxPrice) > 0 { 119 price = new(big.Int).Set(maxPrice) 120 } 121 122 gpo.cacheLock.Lock() 123 gpo.lastHead = headHash 124 gpo.lastPrice = price 125 gpo.cacheLock.Unlock() 126 return price, nil 127 } 128 129 type getBlockPricesResult struct { 130 price *big.Int 131 err error 132 } 133 134 type transactionsByGasPrice []*types.Transaction 135 136 func (t transactionsByGasPrice) Len() int { return len(t) } 137 func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 138 func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 } 139 140 func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) { 141 block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) 142 if block == nil { 143 ch <- getBlockPricesResult{nil, err} 144 return 145 } 146 147 blockTxs := block.Transactions() 148 txs := make([]*types.Transaction, len(blockTxs)) 149 copy(txs, blockTxs) 150 sort.Sort(transactionsByGasPrice(txs)) 151 152 for _, tx := range txs { 153 sender, err := types.Sender(signer, tx) 154 if err == nil && sender != block.Coinbase() { 155 ch <- getBlockPricesResult{tx.GasPrice(), nil} 156 return 157 } 158 } 159 ch <- getBlockPricesResult{nil, nil} 160 } 161 162 type bigIntArray []*big.Int 163 164 func (s bigIntArray) Len() int { return len(s) } 165 func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } 166 func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }