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"
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()
20 baseTxSize := int64(len(data))
22 // extra tx size for sign witness parts
23 signSize := estimateSignSizeForMainchain(template.SigningInstructions)
25 // total gas for tx storage
26 totalTxSizeGas, ok := checked.MulInt64(baseTxSize+signSize, consensus.StorageGasRate)
28 return nil, errors.New("calculate txsize gas got a math error")
31 // consume gas for run VM
32 totalP2WPKHGas := int64(0)
33 totalP2WSHGas := int64(0)
34 baseP2WPKHGas := int64(1419)
36 for pos, inpID := range template.Transaction.Tx.InputIDs {
37 sp, err := template.Transaction.Spend(inpID)
42 resOut, err := template.Transaction.Output(*sp.SpentOutputId)
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)
56 totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas
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)
65 return &EstimateTxGasResp{
66 TotalNeu: estimateNeu,
67 StorageNeu: totalTxSizeGas * consensus.VMGasRate,
68 VMNeu: (totalP2WPKHGas + totalP2WSHGas) * consensus.VMGasRate,
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 {
77 baseP2WSHGas := int64(738)
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)
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 {
94 baseWitnessSize := int64(300)
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