OSDN Git Service

Hulk did something
[bytom/vapor.git] / blockchain / txbuilder / estimate.go
1 package txbuilder
2
3 import (
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"
8 )
9
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"`
16 }
17
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
35                         }
36
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
41                                 totalIssueGas += 5
42                         }
43                         baseIssueSize, baseIssueGas := estimateIssueGas(template.SigningInstructions[pos])
44                         totalWitnessSize += baseIssueSize
45                         totalIssueGas += baseIssueGas
46                 }
47         }
48
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
57         }
58
59         // the total transaction storage gas
60         totalTxSizeGas := (int64(template.Transaction.TxData.SerializedSize) + totalWitnessSize) * consensus.StorageGasRate
61
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,
69         }, nil
70 }
71
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 {
81                                 gas += 27
82                         }
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 {
87                                 gas += 27
88                         }
89                 }
90         }
91         return witnessSize, gas
92 }
93
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
105                 }
106         }
107         return witnessSize, gas
108 }