/
gasstattion.go
125 lines (109 loc) · 3.54 KB
/
gasstattion.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright (c) 2018 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.
package explorer
import (
"math/big"
"sort"
"github.com/pkg/errors"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/blockchain"
"github.com/iotexproject/iotex-core/config"
"github.com/iotexproject/iotex-core/explorer/idl/explorer"
)
// GasStation provide gas related api
type GasStation struct {
bc blockchain.Blockchain
cfg config.Explorer
}
// SuggestGasPrice suggest gas price
func (gs *GasStation) suggestGasPrice() (int64, error) {
var smallestPrices []*big.Int
tip := gs.bc.TipHeight()
endBlockHeight := uint64(0)
if tip > uint64(gs.cfg.GasStation.SuggestBlockWindow) {
endBlockHeight = tip - uint64(gs.cfg.GasStation.SuggestBlockWindow)
}
for height := tip; height > endBlockHeight; height-- {
blk, err := gs.bc.GetBlockByHeight(height)
if err != nil {
return int64(gs.cfg.GasStation.DefaultGas), err
}
if len(blk.Actions) == 0 {
continue
}
smallestPrice := blk.Actions[0].GasPrice()
for _, action := range blk.Actions {
if smallestPrice.Cmp(action.GasPrice()) == 1 {
smallestPrice = action.GasPrice()
}
}
smallestPrices = append(smallestPrices, smallestPrice)
}
if len(smallestPrices) == 0 {
// return default price
return int64(gs.cfg.GasStation.DefaultGas), nil
}
sort.Sort(bigIntArray(smallestPrices))
gasPrice := smallestPrices[(len(smallestPrices)-1)*gs.cfg.GasStation.Percentile/100].Int64()
if gasPrice < int64(gs.cfg.GasStation.DefaultGas) {
gasPrice = int64(gs.cfg.GasStation.DefaultGas)
}
return gasPrice, nil
}
// EstimateGasForTransfer estimate gas for transfer
func (gs *GasStation) estimateGasForTransfer(tsfJSON explorer.SendTransferRequest) (int64, error) {
actPb, err := convertExplorerTransferToActionPb(&tsfJSON, gs.cfg.MaxTransferPayloadBytes)
if err != nil {
return 0, err
}
tsf := &action.SealedEnvelope{}
if err = tsf.LoadProto(actPb); err != nil {
return 0, err
}
gas, err := tsf.IntrinsicGas()
if err != nil {
return 0, err
}
return int64(gas), nil
}
// EstimateGasForVote suggest gas for vote
func (gs *GasStation) estimateGasForVote() (int64, error) {
v := &action.Vote{}
gas, err := v.IntrinsicGas()
if err != nil {
return 0, err
}
return int64(gas), nil
}
// EstimateGasForSmartContract suggest gas for smart contract
func (gs *GasStation) estimateGasForSmartContract(execution explorer.Execution) (int64, error) {
actPb, err := convertExplorerExecutionToActionPb(&execution)
if err != nil {
return 0, err
}
selp := &action.SealedEnvelope{}
if err := selp.LoadProto(actPb); err != nil {
return 0, err
}
sc, ok := selp.Action().(*action.Execution)
if !ok {
return 0, errors.New("not execution")
}
callerAddr, err := address.FromBytes(selp.SrcPubkey().Hash())
if err != nil {
return 0, err
}
_, receipt, err := gs.bc.ExecuteContractRead(callerAddr, sc)
if err != nil {
return 0, err
}
return int64(receipt.GasConsumed), nil
}
type bigIntArray []*big.Int
func (s bigIntArray) Len() int { return len(s) }
func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }