package txbuilder import ( "github.com/vapor/consensus" "github.com/vapor/consensus/segwit" "github.com/vapor/protocol/bc/types" ) // EstimateTxGasInfo estimate transaction consumed gas type EstimateTxGasInfo struct { TotalNeu int64 `json:"total_neu"` FlexibleNeu int64 `json:"flexible_neu"` StorageNeu int64 `json:"storage_neu"` VMNeu int64 `json:"vm_neu"` } // EstimateTxGas estimate consumed neu for transaction func EstimateTxGas(template Template) (*EstimateTxGasInfo, error) { var baseP2WSHSize, totalWitnessSize, baseP2WSHGas, totalP2WPKHGas, totalP2WSHGas int64 baseSize := int64(176) // inputSize(112) + outputSize(64) baseP2WPKHSize := int64(98) baseP2WPKHGas := int64(1409) for pos, input := range template.Transaction.TxData.Inputs { switch input.InputType() { case types.SpendInputType: controlProgram := input.ControlProgram() if segwit.IsP2WPKHScript(controlProgram) { totalWitnessSize += baseP2WPKHSize totalP2WPKHGas += baseP2WPKHGas } else if segwit.IsP2WSHScript(controlProgram) { baseP2WSHSize, baseP2WSHGas = estimateP2WSHGas(template.SigningInstructions[pos]) totalWitnessSize += baseP2WSHSize totalP2WSHGas += baseP2WSHGas } } } flexibleGas := int64(0) if totalP2WPKHGas > 0 { flexibleGas += baseP2WPKHGas + (baseSize+baseP2WPKHSize)*consensus.ActiveNetParams.StorageGasRate } else if totalP2WSHGas > 0 { flexibleGas += baseP2WSHGas + (baseSize+baseP2WSHSize)*consensus.ActiveNetParams.StorageGasRate } // the total transaction storage gas totalTxSizeGas := (int64(template.Transaction.TxData.SerializedSize) + totalWitnessSize) * consensus.ActiveNetParams.StorageGasRate // the total transaction gas is composed of storage and virtual machines totalGas := totalTxSizeGas + totalP2WPKHGas + totalP2WSHGas + flexibleGas if totalGas > consensus.ActiveNetParams.DefaultGasCredit { totalGas -= consensus.ActiveNetParams.DefaultGasCredit } else { totalGas = 0 } return &EstimateTxGasInfo{ TotalNeu: totalGas * consensus.ActiveNetParams.VMGasRate, FlexibleNeu: flexibleGas * consensus.ActiveNetParams.VMGasRate, StorageNeu: totalTxSizeGas * consensus.ActiveNetParams.VMGasRate, VMNeu: (totalP2WPKHGas + totalP2WSHGas) * consensus.ActiveNetParams.VMGasRate, }, nil } // estimateP2WSH return the witness size and the gas consumed to execute the virtual machine for P2WSH program func estimateP2WSHGas(sigInst *SigningInstruction) (int64, int64) { var witnessSize, gas int64 for _, witness := range sigInst.WitnessComponents { switch t := witness.(type) { case *SignatureWitness: witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum) gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659 if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 { gas += 27 } case *RawTxSigWitness: witnessSize += 33*int64(len(t.Keys)) + 65*int64(t.Quorum) gas += 1131*int64(len(t.Keys)) + 72*int64(t.Quorum) + 659 if int64(len(t.Keys)) == 1 && int64(t.Quorum) == 1 { gas += 27 } } } return witnessSize, gas }