github.com/palcoin-project/palcd@v1.0.0/blockchain/weight.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/palcoin-project/palcd/txscript"
    11  	"github.com/palcoin-project/palcd/wire"
    12  	"github.com/palcoin-project/palcutil"
    13  )
    14  
    15  const (
    16  	// MaxBlockWeight defines the maximum block weight, where "block
    17  	// weight" is interpreted as defined in BIP0141. A block's weight is
    18  	// calculated as the sum of the of bytes in the existing transactions
    19  	// and header, plus the weight of each byte within a transaction. The
    20  	// weight of a "base" byte is 4, while the weight of a witness byte is
    21  	// 1. As a result, for a block to be valid, the BlockWeight MUST be
    22  	// less than, or equal to MaxBlockWeight.
    23  	MaxBlockWeight = 4000000
    24  
    25  	// MaxBlockBaseSize is the maximum number of bytes within a block
    26  	// which can be allocated to non-witness data.
    27  	MaxBlockBaseSize = 1000000
    28  
    29  	// MaxBlockSigOpsCost is the maximum number of signature operations
    30  	// allowed for a block. It is calculated via a weighted algorithm which
    31  	// weights segregated witness sig ops lower than regular sig ops.
    32  	MaxBlockSigOpsCost = 80000
    33  
    34  	// WitnessScaleFactor determines the level of "discount" witness data
    35  	// receives compared to "base" data. A scale factor of 4, denotes that
    36  	// witness data is 1/4 as cheap as regular non-witness data.
    37  	WitnessScaleFactor = 4
    38  
    39  	// MinTxOutputWeight is the minimum possible weight for a transaction
    40  	// output.
    41  	MinTxOutputWeight = WitnessScaleFactor * wire.MinTxOutPayload
    42  
    43  	// MaxOutputsPerBlock is the maximum number of transaction outputs there
    44  	// can be in a block of max weight size.
    45  	MaxOutputsPerBlock = MaxBlockWeight / MinTxOutputWeight
    46  )
    47  
    48  // GetBlockWeight computes the value of the weight metric for a given block.
    49  // Currently the weight metric is simply the sum of the block's serialized size
    50  // without any witness data scaled proportionally by the WitnessScaleFactor,
    51  // and the block's serialized size including any witness data.
    52  func GetBlockWeight(blk *palcutil.Block) int64 {
    53  	msgBlock := blk.MsgBlock()
    54  
    55  	baseSize := msgBlock.SerializeSizeStripped()
    56  	totalSize := msgBlock.SerializeSize()
    57  
    58  	// (baseSize * 3) + totalSize
    59  	return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize)
    60  }
    61  
    62  // GetTransactionWeight computes the value of the weight metric for a given
    63  // transaction. Currently the weight metric is simply the sum of the
    64  // transactions's serialized size without any witness data scaled
    65  // proportionally by the WitnessScaleFactor, and the transaction's serialized
    66  // size including any witness data.
    67  func GetTransactionWeight(tx *palcutil.Tx) int64 {
    68  	msgTx := tx.MsgTx()
    69  
    70  	baseSize := msgTx.SerializeSizeStripped()
    71  	totalSize := msgTx.SerializeSize()
    72  
    73  	// (baseSize * 3) + totalSize
    74  	return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize)
    75  }
    76  
    77  // GetSigOpCost returns the unified sig op cost for the passed transaction
    78  // respecting current active soft-forks which modified sig op cost counting.
    79  // The unified sig op cost for a transaction is computed as the sum of: the
    80  // legacy sig op count scaled according to the WitnessScaleFactor, the sig op
    81  // count for all p2sh inputs scaled by the WitnessScaleFactor, and finally the
    82  // unscaled sig op count for any inputs spending witness programs.
    83  func GetSigOpCost(tx *palcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint, bip16, segWit bool) (int, error) {
    84  	numSigOps := CountSigOps(tx) * WitnessScaleFactor
    85  	if bip16 {
    86  		numP2SHSigOps, err := CountP2SHSigOps(tx, isCoinBaseTx, utxoView)
    87  		if err != nil {
    88  			return 0, nil
    89  		}
    90  		numSigOps += (numP2SHSigOps * WitnessScaleFactor)
    91  	}
    92  
    93  	if segWit && !isCoinBaseTx {
    94  		msgTx := tx.MsgTx()
    95  		for txInIndex, txIn := range msgTx.TxIn {
    96  			// Ensure the referenced output is available and hasn't
    97  			// already been spent.
    98  			utxo := utxoView.LookupEntry(txIn.PreviousOutPoint)
    99  			if utxo == nil || utxo.IsSpent() {
   100  				str := fmt.Sprintf("output %v referenced from "+
   101  					"transaction %s:%d either does not "+
   102  					"exist or has already been spent",
   103  					txIn.PreviousOutPoint, tx.Hash(),
   104  					txInIndex)
   105  				return 0, ruleError(ErrMissingTxOut, str)
   106  			}
   107  
   108  			witness := txIn.Witness
   109  			sigScript := txIn.SignatureScript
   110  			pkScript := utxo.PkScript()
   111  			numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness)
   112  		}
   113  
   114  	}
   115  
   116  	return numSigOps, nil
   117  }