github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/eth/gasprice/gasprice.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:37</date> 10 //</624450089061978112> 11 12 13 package gasprice 14 15 import ( 16 "context" 17 "math/big" 18 "sort" 19 "sync" 20 21 "github.com/ethereum/go-ethereum/common" 22 "github.com/ethereum/go-ethereum/core/types" 23 "github.com/ethereum/go-ethereum/internal/ethapi" 24 "github.com/ethereum/go-ethereum/params" 25 "github.com/ethereum/go-ethereum/rpc" 26 ) 27 28 var maxPrice = big.NewInt(500 * params.GWei) 29 30 type Config struct { 31 Blocks int 32 Percentile int 33 Default *big.Int `toml:",omitempty"` 34 } 35 36 //Oracle根据近期的内容建议天然气价格 37 //阻碍。适合轻客户和全客户。 38 type Oracle struct { 39 backend ethapi.Backend 40 lastHead common.Hash 41 lastPrice *big.Int 42 cacheLock sync.RWMutex 43 fetchLock sync.Mutex 44 45 checkBlocks, maxEmpty, maxBlocks int 46 percentile int 47 } 48 49 //new oracle返回新的oracle。 50 func NewOracle(backend ethapi.Backend, params Config) *Oracle { 51 blocks := params.Blocks 52 if blocks < 1 { 53 blocks = 1 54 } 55 percent := params.Percentile 56 if percent < 0 { 57 percent = 0 58 } 59 if percent > 100 { 60 percent = 100 61 } 62 return &Oracle{ 63 backend: backend, 64 lastPrice: params.Default, 65 checkBlocks: blocks, 66 maxEmpty: blocks / 2, 67 maxBlocks: blocks * 5, 68 percentile: percent, 69 } 70 } 71 72 //SuggestPrice返回建议的天然气价格。 73 func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { 74 gpo.cacheLock.RLock() 75 lastHead := gpo.lastHead 76 lastPrice := gpo.lastPrice 77 gpo.cacheLock.RUnlock() 78 79 head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 80 headHash := head.Hash() 81 if headHash == lastHead { 82 return lastPrice, nil 83 } 84 85 gpo.fetchLock.Lock() 86 defer gpo.fetchLock.Unlock() 87 88 //尝试再次检查缓存,可能上次获取的是我们需要的 89 gpo.cacheLock.RLock() 90 lastHead = gpo.lastHead 91 lastPrice = gpo.lastPrice 92 gpo.cacheLock.RUnlock() 93 if headHash == lastHead { 94 return lastPrice, nil 95 } 96 97 blockNum := head.Number.Uint64() 98 ch := make(chan getBlockPricesResult, gpo.checkBlocks) 99 sent := 0 100 exp := 0 101 var blockPrices []*big.Int 102 for sent < gpo.checkBlocks && blockNum > 0 { 103 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 104 sent++ 105 exp++ 106 blockNum-- 107 } 108 maxEmpty := gpo.maxEmpty 109 for exp > 0 { 110 res := <-ch 111 if res.err != nil { 112 return lastPrice, res.err 113 } 114 exp-- 115 if res.price != nil { 116 blockPrices = append(blockPrices, res.price) 117 continue 118 } 119 if maxEmpty > 0 { 120 maxEmpty-- 121 continue 122 } 123 if blockNum > 0 && sent < gpo.maxBlocks { 124 go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch) 125 sent++ 126 exp++ 127 blockNum-- 128 } 129 } 130 price := lastPrice 131 if len(blockPrices) > 0 { 132 sort.Sort(bigIntArray(blockPrices)) 133 price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100] 134 } 135 if price.Cmp(maxPrice) > 0 { 136 price = new(big.Int).Set(maxPrice) 137 } 138 139 gpo.cacheLock.Lock() 140 gpo.lastHead = headHash 141 gpo.lastPrice = price 142 gpo.cacheLock.Unlock() 143 return price, nil 144 } 145 146 type getBlockPricesResult struct { 147 price *big.Int 148 err error 149 } 150 151 type transactionsByGasPrice []*types.Transaction 152 153 func (t transactionsByGasPrice) Len() int { return len(t) } 154 func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 155 func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 } 156 157 //GetBlockPrices计算给定区块中的最低交易天然气价格 158 //并发送到结果通道。如果块为空,则价格为零。 159 func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) { 160 block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) 161 if block == nil { 162 ch <- getBlockPricesResult{nil, err} 163 return 164 } 165 166 blockTxs := block.Transactions() 167 txs := make([]*types.Transaction, len(blockTxs)) 168 copy(txs, blockTxs) 169 sort.Sort(transactionsByGasPrice(txs)) 170 171 for _, tx := range txs { 172 sender, err := types.Sender(signer, tx) 173 if err == nil && sender != block.Coinbase() { 174 ch <- getBlockPricesResult{tx.GasPrice(), nil} 175 return 176 } 177 } 178 ch <- getBlockPricesResult{nil, nil} 179 } 180 181 type bigIntArray []*big.Int 182 183 func (s bigIntArray) Len() int { return len(s) } 184 func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } 185 func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 186