package api import ( "math" "github.com/vapor/blockchain/txbuilder/mainchain" "github.com/vapor/consensus" "github.com/vapor/consensus/segwit" "github.com/vapor/errors" "github.com/vapor/math/checked" ) // EstimateTxGas estimate consumed neu for transaction func EstimateTxGasForMainchain(template mainchain.Template) (*EstimateTxGasResp, error) { // base tx size and not include sign data, err := template.Transaction.TxData.MarshalText() if err != nil { return nil, err } baseTxSize := int64(len(data)) // extra tx size for sign witness parts signSize := estimateSignSizeForMainchain(template.SigningInstructions) // total gas for tx storage totalTxSizeGas, ok := checked.MulInt64(baseTxSize+signSize, consensus.StorageGasRate) if !ok { return nil, errors.New("calculate txsize gas got a math error") } // consume gas for run VM totalP2WPKHGas := int64(0) totalP2WSHGas := int64(0) baseP2WPKHGas := int64(1419) for pos, inpID := range template.Transaction.Tx.InputIDs { sp, err := template.Transaction.Spend(inpID) if err != nil { continue } resOut, err := template.Transaction.Output(*sp.SpentOutputId) if err != nil { continue } if segwit.IsP2WPKHScript(resOut.ControlProgram.Code) { totalP2WPKHGas += baseP2WPKHGas } else if segwit.IsP2WSHScript(resOut.ControlProgram.Code) { sigInst := template.SigningInstructions[pos] totalP2WSHGas += estimateP2WSHGasForMainchain(sigInst) } } // total estimate gas totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas // rounding totalNeu with base rate 100000 totalNeu := float64(totalGas*consensus.VMGasRate) / defaultBaseRate roundingNeu := math.Ceil(totalNeu) estimateNeu := int64(roundingNeu) * int64(defaultBaseRate) // TODO add priority return &EstimateTxGasResp{ TotalNeu: estimateNeu, StorageNeu: totalTxSizeGas * consensus.VMGasRate, VMNeu: (totalP2WPKHGas + totalP2WSHGas) * consensus.VMGasRate, }, nil } // estimate p2wsh gas. // OP_CHECKMULTISIG consume (984 * a - 72 * b - 63) gas, // where a represent the num of public keys, and b represent the num of quorum. func estimateP2WSHGasForMainchain(sigInst *mainchain.SigningInstruction) int64 { P2WSHGas := int64(0) baseP2WSHGas := int64(738) for _, witness := range sigInst.WitnessComponents { switch t := witness.(type) { case *mainchain.SignatureWitness: P2WSHGas += baseP2WSHGas + (984*int64(len(t.Keys)) - 72*int64(t.Quorum) - 63) case *mainchain.RawTxSigWitness: P2WSHGas += baseP2WSHGas + (984*int64(len(t.Keys)) - 72*int64(t.Quorum) - 63) } } return P2WSHGas } // estimate signature part size. // if need multi-sign, calculate the size according to the length of keys. func estimateSignSizeForMainchain(signingInstructions []*mainchain.SigningInstruction) int64 { signSize := int64(0) baseWitnessSize := int64(300) for _, sigInst := range signingInstructions { for _, witness := range sigInst.WitnessComponents { switch t := witness.(type) { case *mainchain.SignatureWitness: signSize += int64(t.Quorum) * baseWitnessSize case *mainchain.RawTxSigWitness: signSize += int64(t.Quorum) * baseWitnessSize } } } return signSize }