wallet/size: add EstimateVirtualSize

This commit adds a new method EstimateVirtualSize that calculates
the worst case estimate vsize for a transaction with a given set
of inputs and outputs. This method is aware of P2PKH, P2WPKH and
P2SH-P2WPKH inputs, and caulculates the transaction vsize with
the witness data included.
This commit is contained in:
Johan T. Halseth 2018-02-15 14:54:04 +01:00 committed by Olaoluwa Osuntokun
parent 2bd4130581
commit f782f9dc68

View file

@ -5,6 +5,7 @@
package txsizes
import (
"github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/wire"
h "github.com/roasbeef/btcwallet/internal/helpers"
@ -52,7 +53,69 @@ const (
// - 25 bytes P2PKH output script
P2PKHOutputSize = 8 + 1 + P2PKHPkScriptSize
// TODO(roasbeef): add estimates for nested p2wkh and p2pkh
// P2WPKHPkScriptSize is the size of a transaction output script that
// pays to a witness pubkey hash. It is calculated as:
//
// - OP_0
// - OP_DATA_20
// - 20 bytes pubkey hash
P2WPKHPkScriptSize = 1 + 1 + 20
// P2WPKHOutputSize is the serialize size of a transaction output with a
// P2WPKH output script. It is calculated as:
//
// - 8 bytes output value
// - 1 byte compact int encoding value 22
// - 22 bytes P2PKH output script
P2WPKHOutputSize = 8 + 1 + P2WPKHPkScriptSize
// RedeemP2WPKHScriptSize is the size of a transaction input script
// that spends a pay-to-witness-public-key hash (P2WPKH). The redeem
// script for P2WPKH spends MUST be empty.
RedeemP2WPKHScriptSize = 0
// RedeemP2WPKHInputSize is the worst case size of a transaction
// input redeeming a P2WPKH output. It is calculated as:
//
// - 32 bytes previous tx
// - 4 bytes output index
// - 1 byte encoding empty redeem script
// - 0 bytes redeem script
// - 4 bytes sequence
RedeemP2WPKHInputSize = 32 + 4 + 1 + RedeemP2WPKHScriptSize + 4
// RedeemNestedP2WPKHScriptSize is the worst case size of a transaction
// input script that redeems a pay-to-witness-key hash nested in P2SH
// (P2SH-P2WPKH). It is calculated as:
//
// - 1 byte compact int encoding value 22
// - OP_0
// - 1 byte compact int encoding value 20
// - 20 byte key hash
RedeemNestedP2WPKHScriptSize = 1 + 1 + 1 + 20
// RedeemNestedP2WPKHInputSize is the worst case size of a
// transaction input redeeming a P2SH-P2WPKH output. It is
// calculated as:
//
// - 32 bytes previous tx
// - 4 bytes output index
// - 1 byte compact int encoding value 23
// - 23 bytes redeem script (scriptSig)
// - 4 bytes sequence
RedeemNestedP2WPKHInputSize = 32 + 4 + 1 +
RedeemNestedP2WPKHScriptSize + 4
// RedeemP2WPKHInputWitnessWeight is the worst case weight of
// a witness for spending P2WPKH and nested P2WPKH outputs. It
// is calculated as:
//
// - 1 wu compact int encoding value 2 (number of items)
// - 1 wu compact int encoding value 73
// - 72 wu DER signature + 1 wu sighash
// - 1 wu compact int encoding value 33
// - 33 wu serialized compressed pubkey
RedeemP2WPKHInputWitnessWeight = 1 + 1 + 73 + 1 + 33
)
// EstimateSerializeSize returns a worst case serialize size estimate for a
@ -74,3 +137,48 @@ func EstimateSerializeSize(inputCount int, txOuts []*wire.TxOut, addChangeOutput
h.SumOutputSerializeSizes(txOuts) +
changeSize
}
// EstimateVirtualSize returns a worst case virtual size estimate for a
// signed transaction that spends the given number of P2PKH, P2WPKH and
// (nested) P2SH-P2WPKH outputs, and contains each transaction output
// from txOuts. The estimate is incremented for an additional P2PKH
// change output if addChangeOutput is true.
func EstimateVirtualSize(numP2PKHIns, numP2WPKHIns, numNestedP2WPKHIns int,
txOuts []*wire.TxOut, addChangeOutput bool) int {
changeSize := 0
outputCount := len(txOuts)
if addChangeOutput {
// We are always using P2WPKH as change output.
changeSize = P2WPKHOutputSize
outputCount++
}
// Version 4 bytes + LockTime 4 bytes + Serialized var int size for the
// number of transaction inputs and outputs + size of redeem scripts +
// the size out the serialized outputs and change.
baseSize := 8 +
wire.VarIntSerializeSize(
uint64(numP2PKHIns+numP2WPKHIns+numNestedP2WPKHIns)) +
wire.VarIntSerializeSize(uint64(len(txOuts))) +
numP2PKHIns*RedeemP2PKHInputSize +
numP2WPKHIns*RedeemP2WPKHInputSize +
numNestedP2WPKHIns*RedeemNestedP2WPKHInputSize +
h.SumOutputSerializeSizes(txOuts) +
changeSize
// If this transaction has any witness inputs, we must count the
// witness data.
witnessWeight := 0
if numP2WPKHIns+numNestedP2WPKHIns > 0 {
// Additional 2 weight units for segwit marker + flag.
witnessWeight = 2 +
wire.VarIntSerializeSize(
uint64(numP2WPKHIns+numNestedP2WPKHIns)) +
numP2WPKHIns*RedeemP2WPKHInputWitnessWeight +
numNestedP2WPKHIns*RedeemP2WPKHInputWitnessWeight
}
// We add 3 to the witness weight to make sure the result is
// always rounded up.
return baseSize + (witnessWeight+3)/blockchain.WitnessScaleFactor
}