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 }