-
Notifications
You must be signed in to change notification settings - Fork 199
/
transactionCostEstimator.go
134 lines (116 loc) · 3.86 KB
/
transactionCostEstimator.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
126
127
128
129
130
131
132
133
134
package transaction
import (
"sync"
"github.com/ElrondNetwork/elrond-go/core"
"github.com/ElrondNetwork/elrond-go/core/check"
"github.com/ElrondNetwork/elrond-go/data/transaction"
"github.com/ElrondNetwork/elrond-go/node/external"
"github.com/ElrondNetwork/elrond-go/process"
)
type transactionCostEstimator struct {
txTypeHandler process.TxTypeHandler
feeHandler process.FeeHandler
query external.SCQueryService
storePerByteCost uint64
compilePerByteCost uint64
mutExecution sync.RWMutex
}
// NewTransactionCostEstimator will create a new transaction cost estimator
func NewTransactionCostEstimator(
txTypeHandler process.TxTypeHandler,
feeHandler process.FeeHandler,
query external.SCQueryService,
gasSchedule core.GasScheduleNotifier,
) (*transactionCostEstimator, error) {
if check.IfNil(txTypeHandler) {
return nil, process.ErrNilTxTypeHandler
}
if check.IfNil(feeHandler) {
return nil, process.ErrNilEconomicsFeeHandler
}
if check.IfNil(query) {
return nil, external.ErrNilSCQueryService
}
compileCost, storeCost := getOperationCost(gasSchedule.LatestGasSchedule())
return &transactionCostEstimator{
txTypeHandler: txTypeHandler,
feeHandler: feeHandler,
query: query,
storePerByteCost: compileCost,
compilePerByteCost: storeCost,
}, nil
}
// GasScheduleChange is called when gas schedule is changed, thus all contracts must be updated
func (tce *transactionCostEstimator) GasScheduleChange(gasSchedule map[string]map[string]uint64) {
tce.mutExecution.Lock()
tce.compilePerByteCost, tce.storePerByteCost = getOperationCost(gasSchedule)
tce.mutExecution.Unlock()
}
func getOperationCost(gasSchedule map[string]map[string]uint64) (uint64, uint64) {
baseOpMap, ok := gasSchedule[core.BaseOperationCost]
if !ok {
return 0, 0
}
storeCost, ok := baseOpMap["StorePerByte"]
if !ok {
return 0, 0
}
compilerCost, ok := baseOpMap["CompilePerByte"]
if !ok {
return 0, 0
}
return storeCost, compilerCost
}
// ComputeTransactionGasLimit will calculate how many gas units a transaction will consume
func (tce *transactionCostEstimator) ComputeTransactionGasLimit(tx *transaction.Transaction) (*transaction.CostResponse, error) {
tce.mutExecution.RLock()
defer tce.mutExecution.RUnlock()
txType, _ := tce.txTypeHandler.ComputeTransactionType(tx)
tx.GasPrice = 1
switch txType {
case process.MoveBalance:
return &transaction.CostResponse{
GasUnits: tce.feeHandler.ComputeGasLimit(tx),
}, nil
case process.SCDeployment:
return tce.computeScDeployGasLimit(tx)
case process.SCInvoking:
return tce.computeScCallGasLimit(tx)
case process.BuiltInFunctionCall:
return tce.computeScCallGasLimit(tx)
case process.RelayedTx:
return &transaction.CostResponse{
GasUnits: 0,
RetMessage: "cannot compute cost of the relayed transaction",
}, nil
default:
return &transaction.CostResponse{
GasUnits: 0,
RetMessage: process.ErrWrongTransaction.Error(),
}, nil
}
}
func (tce *transactionCostEstimator) computeScDeployGasLimit(tx *transaction.Transaction) (*transaction.CostResponse, error) {
scDeployCost := uint64(len(tx.Data)) * (tce.storePerByteCost + tce.compilePerByteCost)
baseCost := tce.feeHandler.ComputeGasLimit(tx)
return &transaction.CostResponse{
GasUnits: baseCost + scDeployCost,
}, nil
}
func (tce *transactionCostEstimator) computeScCallGasLimit(tx *transaction.Transaction) (*transaction.CostResponse, error) {
scCallGasLimit, err := tce.query.ComputeScCallGasLimit(tx)
if err != nil {
return &transaction.CostResponse{
GasUnits: 0,
RetMessage: err.Error(),
}, nil
}
baseCost := tce.feeHandler.ComputeGasLimit(tx)
return &transaction.CostResponse{
GasUnits: baseCost + scCallGasLimit,
}, nil
}
// IsInterfaceNil returns true if there is no value under the interface
func (tce *transactionCostEstimator) IsInterfaceNil() bool {
return tce == nil
}