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
5package blockchain
6
7import (
8	"fmt"
9
10	"github.com/btcsuite/btcd/txscript"
11	"github.com/btcsuite/btcd/wire"
12	"github.com/btcsuite/btcutil"
13)
14
15const (
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.
52func GetBlockWeight(blk *btcutil.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.
67func GetTransactionWeight(tx *btcutil.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.
83func GetSigOpCost(tx *btcutil.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}
118