4 "github.com/vapor/consensus"
5 "github.com/vapor/consensus/segwit"
6 "github.com/vapor/protocol/bc/types"
7 "github.com/vapor/protocol/vm/vmutil"
10 // EstimateTxGasInfo estimate transaction consumed gas
11 type EstimateTxGasInfo struct {
12 TotalNeu int64 `json:"total_neu"`
13 FlexibleNeu int64 `json:"flexible_neu"`
14 StorageNeu int64 `json:"storage_neu"`
15 VMNeu int64 `json:"vm_neu"`
18 // EstimateTxGas estimate consumed neu for transaction
19 func EstimateTxGas(template Template) (*EstimateTxGasInfo, error) {
20 var baseP2WSHSize, totalWitnessSize, baseP2WSHGas, totalP2WPKHGas, totalP2WSHGas, totalIssueGas int64
21 baseSize := int64(176) // inputSize(112) + outputSize(64)
22 baseP2WPKHSize := int64(98)
23 baseP2WPKHGas := int64(1409)
24 for pos, input := range template.Transaction.TxData.Inputs {
25 switch input.InputType() {
26 case types.SpendInputType:
27 controlProgram := input.ControlProgram()
28 if segwit.IsP2WPKHScript(controlProgram) {
29 totalWitnessSize += baseP2WPKHSize
30 totalP2WPKHGas += baseP2WPKHGas
31 } else if segwit.IsP2WSHScript(controlProgram) {
32 baseP2WSHSize, baseP2WSHGas = estimateP2WSHGas(template.SigningInstructions[pos])
33 totalWitnessSize += baseP2WSHSize
34 totalP2WSHGas += baseP2WSHGas
37 case types.IssuanceInputType:
38 issuanceProgram := input.IssuanceProgram()
39 if height := vmutil.GetIssuanceProgramRestrictHeight(issuanceProgram); height > 0 {
40 // the gas for issue program with checking block height
43 baseIssueSize, baseIssueGas := estimateIssueGas(template.SigningInstructions[pos])
44 totalWitnessSize += baseIssueSize
45 totalIssueGas += baseIssueGas
49 flexibleGas := int64(0)
50 if totalP2WPKHGas > 0 {
51 flexibleGas += baseP2WPKHGas + (baseSize+baseP2WPKHSize)*consensus.StorageGasRate
52 } else if totalP2WSHGas > 0 {
53 flexibleGas += baseP2WSHGas + (baseSize+baseP2WSHSize)*consensus.StorageGasRate
54 } else if totalIssueGas > 0 {
55 totalIssueGas += baseP2WPKHGas
56 totalWitnessSize += baseSize + baseP2WPKHSize
59 // the total transaction storage gas
60 totalTxSizeGas := (int64(template.Transaction.TxData.SerializedSize) + totalWitnessSize) * consensus.StorageGasRate
62 // the total transaction gas is composed of storage and virtual machines
63 totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas + totalIssueGas + flexibleGas
64 return &EstimateTxGasInfo{
65 TotalNeu: totalGas * consensus.VMGasRate,
66 FlexibleNeu: flexibleGas * consensus.VMGasRate,
67 StorageNeu: totalTxSizeGas * consensus.VMGasRate,
68 VMNeu: (totalP2WPKHGas + totalP2WSHGas + totalIssueGas) * consensus.VMGasRate,
72 // estimateP2WSH return the witness size and the gas consumed to execute the virtual machine for P2WSH program
73 func estimateP2WSHGas(sigInst *SigningInstruction) (int64, int64) {
74 var witnessSize, gas int64
75 for _, witness := range sigInst.WitnessComponents {
76 switch t := witness.(type) {
77 case *SignatureWitness:
78 witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum)
79 gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659
80 if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 {
83 case *RawTxSigWitness:
84 witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum)
85 gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659
86 if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 {
91 return witnessSize, gas
94 // estimateIssueGas return the witness size and the gas consumed to execute the virtual machine for issuance program
95 func estimateIssueGas(sigInst *SigningInstruction) (int64, int64) {
96 var witnessSize, gas int64
97 for _, witness := range sigInst.WitnessComponents {
98 switch t := witness.(type) {
99 case *SignatureWitness:
100 witnessSize += 65 * int64(t.Quorum)
101 gas += 1065*int64(len(t.Keys)) + 72*int64(t.Quorum) + 316
102 case *RawTxSigWitness:
103 witnessSize += 65 * int64(t.Quorum)
104 gas += 1065*int64(len(t.Keys)) + 72*int64(t.Quorum) + 316
107 return witnessSize, gas