OSDN Git Service

fix(chain-tx): fix omitempty in estimate_gas resp (#1792)
[bytom/bytom.git] / blockchain / txbuilder / estimate.go
1 package txbuilder
2
3 import (
4         "github.com/bytom/consensus"
5         "github.com/bytom/consensus/segwit"
6         "github.com/bytom/protocol/bc/types"
7         "github.com/bytom/protocol/vm/vmutil"
8 )
9
10 const (
11         baseSize       = int64(176) // inputSize(112) + outputSize(64)
12         baseP2WPKHSize = int64(98)
13         baseP2WPKHGas  = int64(1409)
14 )
15
16 var (
17         //ChainTxUtxoNum maximum utxo quantity in a tx
18         ChainTxUtxoNum = 5
19         //ChainTxMergeGas chain tx gas
20         ChainTxMergeGas = uint64(10000000)
21 )
22
23 // EstimateTxGasInfo estimate transaction consumed gas
24 type EstimateTxGasInfo struct {
25         TotalNeu    int64 `json:"total_neu"`
26         FlexibleNeu int64 `json:"flexible_neu"`
27         StorageNeu  int64 `json:"storage_neu"`
28         VMNeu       int64 `json:"vm_neu"`
29         ChainTxNeu  int64 `json:"chain_tx_neu"`
30 }
31
32 func EstimateChainTxGas(templates []Template) (*EstimateTxGasInfo, error) {
33         estimated, err := EstimateTxGas(templates[len(templates)-1])
34         if err != nil {
35                 return nil, err
36         }
37
38         if len(templates) > 1 {
39                 estimated.ChainTxNeu = int64(ChainTxMergeGas) * int64(len(templates)-1)
40         }
41         return estimated, nil
42 }
43
44 // EstimateTxGas estimate consumed neu for transaction
45 func EstimateTxGas(template Template) (*EstimateTxGasInfo, error) {
46         var baseP2WSHSize, totalWitnessSize, baseP2WSHGas, totalP2WPKHGas, totalP2WSHGas, totalIssueGas int64
47         for pos, input := range template.Transaction.TxData.Inputs {
48                 switch input.InputType() {
49                 case types.SpendInputType:
50                         controlProgram := input.ControlProgram()
51                         if segwit.IsP2WPKHScript(controlProgram) {
52                                 totalWitnessSize += baseP2WPKHSize
53                                 totalP2WPKHGas += baseP2WPKHGas
54                         } else if segwit.IsP2WSHScript(controlProgram) {
55                                 baseP2WSHSize, baseP2WSHGas = estimateP2WSHGas(template.SigningInstructions[pos])
56                                 totalWitnessSize += baseP2WSHSize
57                                 totalP2WSHGas += baseP2WSHGas
58                         }
59
60                 case types.IssuanceInputType:
61                         issuanceProgram := input.IssuanceProgram()
62                         if height := vmutil.GetIssuanceProgramRestrictHeight(issuanceProgram); height > 0 {
63                                 // the gas for issue program with checking block height
64                                 totalIssueGas += 5
65                         }
66                         baseIssueSize, baseIssueGas := estimateIssueGas(template.SigningInstructions[pos])
67                         totalWitnessSize += baseIssueSize
68                         totalIssueGas += baseIssueGas
69                 }
70         }
71
72         flexibleGas := int64(0)
73         if totalP2WPKHGas > 0 {
74                 flexibleGas += baseP2WPKHGas + (baseSize+baseP2WPKHSize)*consensus.StorageGasRate
75         } else if totalP2WSHGas > 0 {
76                 flexibleGas += baseP2WSHGas + (baseSize+baseP2WSHSize)*consensus.StorageGasRate
77         } else if totalIssueGas > 0 {
78                 totalIssueGas += baseP2WPKHGas
79                 totalWitnessSize += baseSize + baseP2WPKHSize
80         }
81
82         // the total transaction storage gas
83         totalTxSizeGas := (int64(template.Transaction.TxData.SerializedSize) + totalWitnessSize) * consensus.StorageGasRate
84
85         // the total transaction gas is composed of storage and virtual machines
86         totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas + totalIssueGas + flexibleGas
87         return &EstimateTxGasInfo{
88                 TotalNeu:    totalGas * consensus.VMGasRate,
89                 FlexibleNeu: flexibleGas * consensus.VMGasRate,
90                 StorageNeu:  totalTxSizeGas * consensus.VMGasRate,
91                 VMNeu:       (totalP2WPKHGas + totalP2WSHGas + totalIssueGas) * consensus.VMGasRate,
92         }, nil
93 }
94
95 // estimateP2WSH return the witness size and the gas consumed to execute the virtual machine for P2WSH program
96 func estimateP2WSHGas(sigInst *SigningInstruction) (int64, int64) {
97         var witnessSize, gas int64
98         for _, witness := range sigInst.WitnessComponents {
99                 switch t := witness.(type) {
100                 case *SignatureWitness:
101                         witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum)
102                         gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659
103                         if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 {
104                                 gas += 27
105                         }
106                 case *RawTxSigWitness:
107                         witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum)
108                         gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659
109                         if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 {
110                                 gas += 27
111                         }
112                 }
113         }
114         return witnessSize, gas
115 }
116
117 // estimateIssueGas return the witness size and the gas consumed to execute the virtual machine for issuance program
118 func estimateIssueGas(sigInst *SigningInstruction) (int64, int64) {
119         var witnessSize, gas int64
120         for _, witness := range sigInst.WitnessComponents {
121                 switch t := witness.(type) {
122                 case *SignatureWitness:
123                         witnessSize += 65 * int64(t.Quorum)
124                         gas += 1065*int64(len(t.Keys)) + 72*int64(t.Quorum) + 316
125                 case *RawTxSigWitness:
126                         witnessSize += 65 * int64(t.Quorum)
127                         gas += 1065*int64(len(t.Keys)) + 72*int64(t.Quorum) + 316
128                 }
129         }
130         return witnessSize, gas
131 }