github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/txbuilder/estimate.go (about) 1 package txbuilder 2 3 import ( 4 "github.com/bytom/bytom/consensus" 5 "github.com/bytom/bytom/consensus/segwit" 6 "github.com/bytom/bytom/protocol/bc/types" 7 "github.com/bytom/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(6000000) 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.ControlProgram() 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 }