github.com/klaytn/klaytn@v1.10.2/node/cn/gasprice/gasprice.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of go-ethereum. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from eth/gasprice/gasprice.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package gasprice 22 23 import ( 24 "context" 25 "math/big" 26 "sync" 27 28 "github.com/klaytn/klaytn/blockchain/types" 29 "github.com/klaytn/klaytn/networks/rpc" 30 31 "github.com/klaytn/klaytn/common" 32 "github.com/klaytn/klaytn/params" 33 ) 34 35 var maxPrice = big.NewInt(500 * params.Ston) 36 37 type Config struct { 38 Blocks int 39 Percentile int 40 MaxHeaderHistory int 41 MaxBlockHistory int 42 Default *big.Int `toml:",omitempty"` 43 } 44 45 // OracleBackend includes all necessary background APIs for oracle. 46 type OracleBackend interface { 47 HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) 48 BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) 49 GetBlockReceipts(ctx context.Context, hash common.Hash) types.Receipts 50 ChainConfig() *params.ChainConfig 51 CurrentBlock() *types.Block 52 } 53 54 type TxPool interface { 55 GasPrice() *big.Int 56 } 57 58 // Oracle recommends gas prices based on the content of recent 59 // blocks. Suitable for both light and full clients. 60 type Oracle struct { 61 backend OracleBackend 62 lastHead common.Hash 63 lastPrice *big.Int 64 cacheLock sync.RWMutex 65 fetchLock sync.Mutex 66 txPool TxPool 67 68 checkBlocks, maxEmpty, maxBlocks int 69 percentile int 70 maxHeaderHistory, maxBlockHistory int 71 } 72 73 // NewOracle returns a new oracle. 74 func NewOracle(backend OracleBackend, params Config, txPool TxPool) *Oracle { 75 blocks := params.Blocks 76 if blocks < 1 { 77 blocks = 1 78 } 79 percent := params.Percentile 80 if percent < 0 { 81 percent = 0 82 } 83 if percent > 100 { 84 percent = 100 85 } 86 return &Oracle{ 87 backend: backend, 88 lastPrice: params.Default, 89 checkBlocks: blocks, 90 maxEmpty: blocks / 2, 91 maxBlocks: blocks * 5, 92 percentile: percent, 93 maxHeaderHistory: params.MaxHeaderHistory, 94 maxBlockHistory: params.MaxBlockHistory, 95 txPool: txPool, 96 } 97 } 98 99 // SuggestPrice returns the recommended gas price. 100 func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { 101 if gpo.txPool == nil { 102 // If txpool is not set, just return 0. This is used for testing. 103 return common.Big0, nil 104 } 105 // Since we have fixed gas price, we can directly get this value from TxPool. 106 suggestedPrice := gpo.txPool.GasPrice() 107 if gpo.backend.ChainConfig().IsMagmaForkEnabled(new(big.Int).Add(gpo.backend.CurrentBlock().Number(), common.Big1)) { 108 return new(big.Int).Mul(suggestedPrice, common.Big2), nil 109 } 110 return suggestedPrice, nil 111 /* 112 // TODO-Klaytn-RemoveLater Later remove below obsolete code if we don't need them anymore. 113 gpo.cacheLock.RLock() 114 lastHead := gpo.lastHead 115 lastPrice := gpo.lastPrice 116 gpo.cacheLock.RUnlock() 117 118 head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 119 headHash := head.Hash() 120 if headHash == lastHead { 121 return lastPrice, nil 122 } 123 124 gpo.fetchLock.Lock() 125 defer gpo.fetchLock.Unlock() 126 127 // try checking the cache again, maybe the last fetch fetched what we need 128 gpo.cacheLock.RLock() 129 lastHead = gpo.lastHead 130 lastPrice = gpo.lastPrice 131 gpo.cacheLock.RUnlock() 132 if headHash == lastHead { 133 return lastPrice, nil 134 } 135 136 blockNum := head.Number.Uint64() 137 ch := make(chan getBlockPricesResult, gpo.checkBlocks) 138 sent := 0 139 exp := 0 140 var blockPrices []*big.Int 141 for sent < gpo.checkBlocks && blockNum > 0 { 142 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 143 sent++ 144 exp++ 145 blockNum-- 146 } 147 maxEmpty := gpo.maxEmpty 148 for exp > 0 { 149 res := <-ch 150 if res.err != nil { 151 return lastPrice, res.err 152 } 153 exp-- 154 if res.price != nil { 155 blockPrices = append(blockPrices, res.price) 156 continue 157 } 158 if maxEmpty > 0 { 159 maxEmpty-- 160 continue 161 } 162 if blockNum > 0 && sent < gpo.maxBlocks { 163 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 164 sent++ 165 exp++ 166 blockNum-- 167 } 168 } 169 price := lastPrice 170 if len(blockPrices) > 0 { 171 sort.Sort(bigIntArray(blockPrices)) 172 price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100] 173 } 174 if price.Cmp(maxPrice) > 0 { 175 price = new(big.Int).Set(maxPrice) 176 } 177 178 gpo.cacheLock.Lock() 179 gpo.lastHead = headHash 180 gpo.lastPrice = price 181 gpo.cacheLock.Unlock() 182 return price, nil 183 */ 184 } 185 186 // TODO-Klaytn-RemoveLater Later remove below obsolete code if we don't need them anymore. 187 //type getBlockPricesResult struct { 188 // price *big.Int 189 // err error 190 //} 191 // 192 //type transactionsByGasPrice []*types.Transaction 193 // 194 //func (t transactionsByGasPrice) Len() int { return len(t) } 195 //func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 196 //func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 } 197 // 198 //type bigIntArray []*big.Int 199 // 200 //func (s bigIntArray) Len() int { return len(s) } 201 //func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } 202 //func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }