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  }