OSDN Git Service

Merge pull request #32 from Bytom/dev
[bytom/vapor.git] / api / estimate.go
1 package api
2
3 import (
4         "math"
5
6         "github.com/vapor/blockchain/txbuilder/mainchain"
7         "github.com/vapor/consensus"
8         "github.com/vapor/consensus/segwit"
9         "github.com/vapor/errors"
10         "github.com/vapor/math/checked"
11 )
12
13 // EstimateTxGas estimate consumed neu for transaction
14 func EstimateTxGasForMainchain(template mainchain.Template) (*EstimateTxGasResp, error) {
15         // base tx size and not include sign
16         data, err := template.Transaction.TxData.MarshalText()
17         if err != nil {
18                 return nil, err
19         }
20         baseTxSize := int64(len(data))
21
22         // extra tx size for sign witness parts
23         signSize := estimateSignSizeForMainchain(template.SigningInstructions)
24
25         // total gas for tx storage
26         totalTxSizeGas, ok := checked.MulInt64(baseTxSize+signSize, consensus.StorageGasRate)
27         if !ok {
28                 return nil, errors.New("calculate txsize gas got a math error")
29         }
30
31         // consume gas for run VM
32         totalP2WPKHGas := int64(0)
33         totalP2WSHGas := int64(0)
34         baseP2WPKHGas := int64(1419)
35
36         for pos, inpID := range template.Transaction.Tx.InputIDs {
37                 sp, err := template.Transaction.Spend(inpID)
38                 if err != nil {
39                         continue
40                 }
41
42                 resOut, err := template.Transaction.Output(*sp.SpentOutputId)
43                 if err != nil {
44                         continue
45                 }
46
47                 if segwit.IsP2WPKHScript(resOut.ControlProgram.Code) {
48                         totalP2WPKHGas += baseP2WPKHGas
49                 } else if segwit.IsP2WSHScript(resOut.ControlProgram.Code) {
50                         sigInst := template.SigningInstructions[pos]
51                         totalP2WSHGas += estimateP2WSHGasForMainchain(sigInst)
52                 }
53         }
54
55         // total estimate gas
56         totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas
57
58         // rounding totalNeu with base rate 100000
59         totalNeu := float64(totalGas*consensus.VMGasRate) / defaultBaseRate
60         roundingNeu := math.Ceil(totalNeu)
61         estimateNeu := int64(roundingNeu) * int64(defaultBaseRate)
62
63         // TODO add priority
64
65         return &EstimateTxGasResp{
66                 TotalNeu:   estimateNeu,
67                 StorageNeu: totalTxSizeGas * consensus.VMGasRate,
68                 VMNeu:      (totalP2WPKHGas + totalP2WSHGas) * consensus.VMGasRate,
69         }, nil
70 }
71
72 // estimate p2wsh gas.
73 // OP_CHECKMULTISIG consume (984 * a - 72 * b - 63) gas,
74 // where a represent the num of public keys, and b represent the num of quorum.
75 func estimateP2WSHGasForMainchain(sigInst *mainchain.SigningInstruction) int64 {
76         P2WSHGas := int64(0)
77         baseP2WSHGas := int64(738)
78
79         for _, witness := range sigInst.WitnessComponents {
80                 switch t := witness.(type) {
81                 case *mainchain.SignatureWitness:
82                         P2WSHGas += baseP2WSHGas + (984*int64(len(t.Keys)) - 72*int64(t.Quorum) - 63)
83                 case *mainchain.RawTxSigWitness:
84                         P2WSHGas += baseP2WSHGas + (984*int64(len(t.Keys)) - 72*int64(t.Quorum) - 63)
85                 }
86         }
87         return P2WSHGas
88 }
89
90 // estimate signature part size.
91 // if need multi-sign, calculate the size according to the length of keys.
92 func estimateSignSizeForMainchain(signingInstructions []*mainchain.SigningInstruction) int64 {
93         signSize := int64(0)
94         baseWitnessSize := int64(300)
95
96         for _, sigInst := range signingInstructions {
97                 for _, witness := range sigInst.WitnessComponents {
98                         switch t := witness.(type) {
99                         case *mainchain.SignatureWitness:
100                                 signSize += int64(t.Quorum) * baseWitnessSize
101                         case *mainchain.RawTxSigWitness:
102                                 signSize += int64(t.Quorum) * baseWitnessSize
103                         }
104                 }
105         }
106         return signSize
107 }