// Copyright (c) 2013-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package blockchain import ( "fmt" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) const ( // MaxBlockWeight defines the maximum block weight, where "block // weight" is interpreted as defined in BIP0141. A block's weight is // calculated as the sum of the of bytes in the existing transactions // and header, plus the weight of each byte within a transaction. The // weight of a "base" byte is 4, while the weight of a witness byte is // 1. As a result, for a block to be valid, the BlockWeight MUST be // less than, or equal to MaxBlockWeight. MaxBlockWeight = 8000000 // MaxBlockBaseSize is the maximum number of bytes within a block // which can be allocated to non-witness data. MaxBlockBaseSize = 8000000 // MaxBlockSigOpsCost is the maximum number of signature operations // allowed for a block. It is calculated via a weighted algorithm which // weights segregated witness sig ops lower than regular sig ops. MaxBlockSigOpsCost = 80000 // WitnessScaleFactor determines the level of "discount" witness data // receives compared to "base" data. A scale factor of 4, denotes that // witness data is 1/4 as cheap as regular non-witness data. WitnessScaleFactor = 4 // MinTxOutputWeight is the minimum possible weight for a transaction // output. MinTxOutputWeight = WitnessScaleFactor * wire.MinTxOutPayload // MaxOutputsPerBlock is the maximum number of transaction outputs there // can be in a block of max weight size. MaxOutputsPerBlock = MaxBlockWeight / MinTxOutputWeight ) // GetBlockWeight computes the value of the weight metric for a given block. // Currently the weight metric is simply the sum of the block's serialized size // without any witness data scaled proportionally by the WitnessScaleFactor, // and the block's serialized size including any witness data. func GetBlockWeight(blk *btcutil.Block) int64 { msgBlock := blk.MsgBlock() baseSize := msgBlock.SerializeSizeStripped() totalSize := msgBlock.SerializeSize() // (baseSize * 3) + totalSize return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize) } // GetTransactionWeight computes the value of the weight metric for a given // transaction. Currently the weight metric is simply the sum of the // transactions's serialized size without any witness data scaled // proportionally by the WitnessScaleFactor, and the transaction's serialized // size including any witness data. func GetTransactionWeight(tx *btcutil.Tx) int64 { msgTx := tx.MsgTx() baseSize := msgTx.SerializeSizeStripped() totalSize := msgTx.SerializeSize() // (baseSize * 3) + totalSize return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize) } // GetSigOpCost returns the unified sig op cost for the passed transaction // respecting current active soft-forks which modified sig op cost counting. // The unified sig op cost for a transaction is computed as the sum of: the // legacy sig op count scaled according to the WitnessScaleFactor, the sig op // count for all p2sh inputs scaled by the WitnessScaleFactor, and finally the // unscaled sig op count for any inputs spending witness programs. func GetSigOpCost(tx *btcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint, bip16, segWit bool) (int, error) { numSigOps := CountSigOps(tx) * WitnessScaleFactor if bip16 { numP2SHSigOps, err := CountP2SHSigOps(tx, isCoinBaseTx, utxoView) if err != nil { return 0, nil } numSigOps += (numP2SHSigOps * WitnessScaleFactor) } if segWit && !isCoinBaseTx { msgTx := tx.MsgTx() for txInIndex, txIn := range msgTx.TxIn { // Ensure the referenced output is available and hasn't // already been spent. utxo := utxoView.LookupEntry(txIn.PreviousOutPoint) if utxo == nil || utxo.IsSpent() { str := fmt.Sprintf("output %v referenced from "+ "transaction %s:%d either does not "+ "exist or has already been spent", txIn.PreviousOutPoint, tx.Hash(), txInIndex) return 0, ruleError(ErrMissingTxOut, str) } witness := txIn.Witness sigScript := txIn.SignatureScript pkScript := utxo.PkScript() numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness) } } return numSigOps, nil }