BIP0141+blockchain: implement tx/block weight calculation funcitons
This commit implements the new “weight” metric introduced as part of the segwit soft-fork. Post-fork activation, rather than limiting the size of blocks and transactions based purely on serialized size, a new metric “weight” will instead be used as a way to more accurately reflect the costs of a tx/block on the system. With blocks constrained by weight, the maximum block-size increases to ~4MB.
This commit is contained in:
parent
a411bbcf85
commit
d38ae9ca0b
4 changed files with 127 additions and 1 deletions
109
blockchain/weight.go
Normal file
109
blockchain/weight.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright (c) 2013-2016 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/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 = 4000000
|
||||||
|
|
||||||
|
// MaxBlockBaseSize is the maximum number of bytes within a block
|
||||||
|
// which can be allocated to non-witness data.
|
||||||
|
MaxBlockBaseSize = 1000000
|
||||||
|
|
||||||
|
// MaxBlockSigOpsCost is the maximum number of signature operations
|
||||||
|
// allowed for a block. It is calculated via a weighted algorithm which
|
||||||
|
// weights segragated 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
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 input transaction is available.
|
||||||
|
originTxHash := &txIn.PreviousOutPoint.Hash
|
||||||
|
originTxIndex := txIn.PreviousOutPoint.Index
|
||||||
|
txEntry := utxoView.LookupEntry(originTxHash)
|
||||||
|
if txEntry == nil || txEntry.IsOutputSpent(originTxIndex) {
|
||||||
|
str := fmt.Sprintf("unable to find unspent output "+
|
||||||
|
"%v referenced from transaction %s:%d",
|
||||||
|
txIn.PreviousOutPoint, tx.Hash(), txInIndex)
|
||||||
|
return 0, ruleError(ErrMissingTx, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
witness := txIn.Witness
|
||||||
|
sigScript := txIn.SignatureScript
|
||||||
|
pkScript := txEntry.PkScriptByIndex(originTxIndex)
|
||||||
|
numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return numSigOps, nil
|
||||||
|
}
|
|
@ -805,7 +805,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
|
||||||
// which is more desirable. Therefore, as long as the size of the
|
// which is more desirable. Therefore, as long as the size of the
|
||||||
// transaction does not exceeed 1000 less than the reserved space for
|
// transaction does not exceeed 1000 less than the reserved space for
|
||||||
// high-priority transactions, don't require a fee for it.
|
// high-priority transactions, don't require a fee for it.
|
||||||
serializedSize := int64(tx.MsgTx().SerializeSize())
|
serializedSize := GetTxVirtualSize(tx)
|
||||||
minFee := calcMinRequiredTxRelayFee(serializedSize,
|
minFee := calcMinRequiredTxRelayFee(serializedSize,
|
||||||
mp.cfg.Policy.MinRelayTxFee)
|
mp.cfg.Policy.MinRelayTxFee)
|
||||||
if serializedSize >= (DefaultBlockPrioritySize-1000) && txFee < minFee {
|
if serializedSize >= (DefaultBlockPrioritySize-1000) && txFee < minFee {
|
||||||
|
@ -1150,6 +1150,7 @@ func (mp *TxPool) RawMempoolVerbose() map[string]*btcjson.GetRawMempoolVerboseRe
|
||||||
|
|
||||||
mpd := &btcjson.GetRawMempoolVerboseResult{
|
mpd := &btcjson.GetRawMempoolVerboseResult{
|
||||||
Size: int32(tx.MsgTx().SerializeSize()),
|
Size: int32(tx.MsgTx().SerializeSize()),
|
||||||
|
Vsize: int32(GetTxVirtualSize(tx)),
|
||||||
Fee: btcutil.Amount(desc.Fee).ToBTC(),
|
Fee: btcutil.Amount(desc.Fee).ToBTC(),
|
||||||
Time: desc.Added.Unix(),
|
Time: desc.Added.Unix(),
|
||||||
Height: int64(desc.Height),
|
Height: int64(desc.Height),
|
||||||
|
|
|
@ -343,3 +343,16 @@ func checkTransactionStandard(tx *btcutil.Tx, height int32,
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTxVirtualSize computes the virtual size of a given transaction. A
|
||||||
|
// transaction's virtual size is based off it's weight, creating a discount for
|
||||||
|
// any witness data it contains, proportional to the current
|
||||||
|
// blockchain.WitnessScaleFactor value.
|
||||||
|
func GetTxVirtualSize(tx *btcutil.Tx) int64 {
|
||||||
|
// vSize := (weight(tx) + 3) / 4
|
||||||
|
// := (((baseSize * 3) + totalSize) + 3) / 4
|
||||||
|
// We add 3 here as a way to compute the ceiling of the prior arithmetic
|
||||||
|
// to 4. The division by 4 creates a discount for wit witness data.
|
||||||
|
return (blockchain.GetTransactionWeight(tx) + (blockchain.WitnessScaleFactor - 1)) /
|
||||||
|
blockchain.WitnessScaleFactor
|
||||||
|
}
|
||||||
|
|
|
@ -720,6 +720,9 @@ func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx,
|
||||||
txReply := &btcjson.TxRawResult{
|
txReply := &btcjson.TxRawResult{
|
||||||
Hex: mtxHex,
|
Hex: mtxHex,
|
||||||
Txid: txHash,
|
Txid: txHash,
|
||||||
|
Hash: mtx.WitnessHash().String(),
|
||||||
|
Size: int32(mtx.SerializeSize()),
|
||||||
|
Vsize: int32(mempool.GetTxVirtualSize(btcutil.NewTx(mtx))),
|
||||||
Vin: createVinList(mtx),
|
Vin: createVinList(mtx),
|
||||||
Vout: createVoutList(mtx, chainParams, nil),
|
Vout: createVoutList(mtx, chainParams, nil),
|
||||||
Version: mtx.Version,
|
Version: mtx.Version,
|
||||||
|
|
Loading…
Reference in a new issue