lbcwallet/wallet/txsizes/size.go

228 lines
7.5 KiB
Go
Raw Normal View History

Refactor wallet transaction creation code. This began as a change to improve the fee calculation code and evolved into a much larger refactor which improves the readability and modularity of all of the transaction creation code. Transaction fee calculations have been switched from full increments of the relay fee to a proportion based on the transaction size. This means that for a relay fee of 1e3 satoshis/kB, a 500 byte transaction is only required to pay a 5e2 satoshi fee and a 1500 byte transaction only need pay a 1.5e3 fee. The previous code would end up estimating these fees to be 1e3 and 2e3 respectively. Because the previous code would add more fee than needed in almost every case, the transaction size estimations were optimistic (best/smallest case) and signing was done in a loop where the fee was incremented by the relay fee again each time the actual size of the signed transaction rendered the fee too low. This has switched to using worst case transaction size estimates rather than best case, and signing is only performed once. Transaction input signature creation has switched from using txscript.SignatureScript to txscript.SignTxOutput. The new API is able to redeem outputs other than just P2PKH, so the previous restrictions about P2SH outputs being unspendable (except through the signrawtransaction RPC) no longer hold. Several new public packages have been added: wallet/txauthor - transaction authoring and signing wallet/txfees - fee estimations and change output inclusion wallet/txrules - simple consensus and mempool policy rule checks Along with some internal packages: wallet/internal/txsizes - transaction size estimation internal/helpers - context free convenience functions The txsizes package is internal as the estimations it provides are specific for the algorithms used by these new packages.
2016-02-28 05:30:56 +01:00
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txsizes
import (
"github.com/lbryio/lbcd/blockchain"
"github.com/lbryio/lbcd/txscript"
"github.com/lbryio/lbcd/wire"
Refactor wallet transaction creation code. This began as a change to improve the fee calculation code and evolved into a much larger refactor which improves the readability and modularity of all of the transaction creation code. Transaction fee calculations have been switched from full increments of the relay fee to a proportion based on the transaction size. This means that for a relay fee of 1e3 satoshis/kB, a 500 byte transaction is only required to pay a 5e2 satoshi fee and a 1500 byte transaction only need pay a 1.5e3 fee. The previous code would end up estimating these fees to be 1e3 and 2e3 respectively. Because the previous code would add more fee than needed in almost every case, the transaction size estimations were optimistic (best/smallest case) and signing was done in a loop where the fee was incremented by the relay fee again each time the actual size of the signed transaction rendered the fee too low. This has switched to using worst case transaction size estimates rather than best case, and signing is only performed once. Transaction input signature creation has switched from using txscript.SignatureScript to txscript.SignTxOutput. The new API is able to redeem outputs other than just P2PKH, so the previous restrictions about P2SH outputs being unspendable (except through the signrawtransaction RPC) no longer hold. Several new public packages have been added: wallet/txauthor - transaction authoring and signing wallet/txfees - fee estimations and change output inclusion wallet/txrules - simple consensus and mempool policy rule checks Along with some internal packages: wallet/internal/txsizes - transaction size estimation internal/helpers - context free convenience functions The txsizes package is internal as the estimations it provides are specific for the algorithms used by these new packages.
2016-02-28 05:30:56 +01:00
)
// Worst case script and input/output size estimates.
const (
// RedeemP2PKHSigScriptSize is the worst case (largest) serialize size
// of a transaction input script that redeems a compressed P2PKH output.
// It is calculated as:
//
// - OP_DATA_73
// - 72 bytes DER signature + 1 byte sighash
// - OP_DATA_33
// - 33 bytes serialized compressed pubkey
RedeemP2PKHSigScriptSize = 1 + 73 + 1 + 33
// P2PKHPkScriptSize is the size of a transaction output script that
// pays to a compressed pubkey hash. It is calculated as:
//
// - OP_DUP
// - OP_HASH160
// - OP_DATA_20
// - 20 bytes pubkey hash
// - OP_EQUALVERIFY
// - OP_CHECKSIG
P2PKHPkScriptSize = 1 + 1 + 1 + 20 + 1 + 1
// RedeemP2PKHInputSize is the worst case (largest) serialize size of a
// transaction input redeeming a compressed P2PKH output. It is
// calculated as:
//
// - 32 bytes previous tx
// - 4 bytes output index
// - 1 byte compact int encoding value 107
// - 107 bytes signature script
// - 4 bytes sequence
RedeemP2PKHInputSize = 32 + 4 + 1 + RedeemP2PKHSigScriptSize + 4
// P2PKHOutputSize is the serialize size of a transaction output with a
// P2PKH output script. It is calculated as:
//
// - 8 bytes output value
// - 1 byte compact int encoding value 25
// - 25 bytes P2PKH output script
P2PKHOutputSize = 8 + 1 + P2PKHPkScriptSize
// 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
// NestedP2WPKHPkScriptSize is the size of a transaction output script
// that pays to a pay-to-witness-key hash nested in P2SH (P2SH-P2WPKH).
// It is calculated as:
//
// - OP_HASH160
// - OP_DATA_20
// - 20 bytes script hash
// - OP_EQUAL
NestedP2WPKHPkScriptSize = 1 + 1 + 20 + 1
// 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
Refactor wallet transaction creation code. This began as a change to improve the fee calculation code and evolved into a much larger refactor which improves the readability and modularity of all of the transaction creation code. Transaction fee calculations have been switched from full increments of the relay fee to a proportion based on the transaction size. This means that for a relay fee of 1e3 satoshis/kB, a 500 byte transaction is only required to pay a 5e2 satoshi fee and a 1500 byte transaction only need pay a 1.5e3 fee. The previous code would end up estimating these fees to be 1e3 and 2e3 respectively. Because the previous code would add more fee than needed in almost every case, the transaction size estimations were optimistic (best/smallest case) and signing was done in a loop where the fee was incremented by the relay fee again each time the actual size of the signed transaction rendered the fee too low. This has switched to using worst case transaction size estimates rather than best case, and signing is only performed once. Transaction input signature creation has switched from using txscript.SignatureScript to txscript.SignTxOutput. The new API is able to redeem outputs other than just P2PKH, so the previous restrictions about P2SH outputs being unspendable (except through the signrawtransaction RPC) no longer hold. Several new public packages have been added: wallet/txauthor - transaction authoring and signing wallet/txfees - fee estimations and change output inclusion wallet/txrules - simple consensus and mempool policy rule checks Along with some internal packages: wallet/internal/txsizes - transaction size estimation internal/helpers - context free convenience functions The txsizes package is internal as the estimations it provides are specific for the algorithms used by these new packages.
2016-02-28 05:30:56 +01:00
)
// SumOutputSerializeSizes sums up the serialized size of the supplied outputs.
func SumOutputSerializeSizes(outputs []*wire.TxOut) (serializeSize int) {
for _, txOut := range outputs {
serializeSize += txOut.SerializeSize()
}
return serializeSize
}
Refactor wallet transaction creation code. This began as a change to improve the fee calculation code and evolved into a much larger refactor which improves the readability and modularity of all of the transaction creation code. Transaction fee calculations have been switched from full increments of the relay fee to a proportion based on the transaction size. This means that for a relay fee of 1e3 satoshis/kB, a 500 byte transaction is only required to pay a 5e2 satoshi fee and a 1500 byte transaction only need pay a 1.5e3 fee. The previous code would end up estimating these fees to be 1e3 and 2e3 respectively. Because the previous code would add more fee than needed in almost every case, the transaction size estimations were optimistic (best/smallest case) and signing was done in a loop where the fee was incremented by the relay fee again each time the actual size of the signed transaction rendered the fee too low. This has switched to using worst case transaction size estimates rather than best case, and signing is only performed once. Transaction input signature creation has switched from using txscript.SignatureScript to txscript.SignTxOutput. The new API is able to redeem outputs other than just P2PKH, so the previous restrictions about P2SH outputs being unspendable (except through the signrawtransaction RPC) no longer hold. Several new public packages have been added: wallet/txauthor - transaction authoring and signing wallet/txfees - fee estimations and change output inclusion wallet/txrules - simple consensus and mempool policy rule checks Along with some internal packages: wallet/internal/txsizes - transaction size estimation internal/helpers - context free convenience functions The txsizes package is internal as the estimations it provides are specific for the algorithms used by these new packages.
2016-02-28 05:30:56 +01:00
// EstimateSerializeSize returns a worst case serialize size estimate for a
// signed transaction that spends inputCount number of compressed P2PKH outputs
// and contains each transaction output from txOuts. The estimated size is
// incremented for an additional P2PKH change output if addChangeOutput is true.
func EstimateSerializeSize(inputCount int, txOuts []*wire.TxOut, addChangeOutput bool) int {
changeSize := 0
outputCount := len(txOuts)
if addChangeOutput {
changeSize = P2PKHOutputSize
outputCount++
}
// 8 additional bytes are for version and locktime
return 8 + wire.VarIntSerializeSize(uint64(inputCount)) +
wire.VarIntSerializeSize(uint64(outputCount)) +
inputCount*RedeemP2PKHInputSize +
SumOutputSerializeSizes(txOuts) +
Refactor wallet transaction creation code. This began as a change to improve the fee calculation code and evolved into a much larger refactor which improves the readability and modularity of all of the transaction creation code. Transaction fee calculations have been switched from full increments of the relay fee to a proportion based on the transaction size. This means that for a relay fee of 1e3 satoshis/kB, a 500 byte transaction is only required to pay a 5e2 satoshi fee and a 1500 byte transaction only need pay a 1.5e3 fee. The previous code would end up estimating these fees to be 1e3 and 2e3 respectively. Because the previous code would add more fee than needed in almost every case, the transaction size estimations were optimistic (best/smallest case) and signing was done in a loop where the fee was incremented by the relay fee again each time the actual size of the signed transaction rendered the fee too low. This has switched to using worst case transaction size estimates rather than best case, and signing is only performed once. Transaction input signature creation has switched from using txscript.SignatureScript to txscript.SignTxOutput. The new API is able to redeem outputs other than just P2PKH, so the previous restrictions about P2SH outputs being unspendable (except through the signrawtransaction RPC) no longer hold. Several new public packages have been added: wallet/txauthor - transaction authoring and signing wallet/txfees - fee estimations and change output inclusion wallet/txrules - simple consensus and mempool policy rule checks Along with some internal packages: wallet/internal/txsizes - transaction size estimation internal/helpers - context free convenience functions The txsizes package is internal as the estimations it provides are specific for the algorithms used by these new packages.
2016-02-28 05:30:56 +01:00
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, changeScriptSize int) int {
outputCount := len(txOuts)
changeOutputSize := 0
if changeScriptSize > 0 {
changeOutputSize = 8 +
wire.VarIntSerializeSize(uint64(changeScriptSize)) +
changeScriptSize
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 +
SumOutputSerializeSizes(txOuts) +
changeOutputSize
// 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
}
2021-03-29 16:36:41 +02:00
// GetMinInputVirtualSize returns the minimum number of vbytes that this input
// adds to a transaction.
func GetMinInputVirtualSize(pkScript []byte) int {
var baseSize, witnessWeight int
switch {
// If this is a p2sh output, we assume this is a
// nested P2WKH.
case txscript.IsPayToScriptHash(pkScript):
baseSize = RedeemNestedP2WPKHInputSize
witnessWeight = RedeemP2WPKHInputWitnessWeight
case txscript.IsPayToWitnessPubKeyHash(pkScript):
baseSize = RedeemP2WPKHInputSize
witnessWeight = RedeemP2WPKHInputWitnessWeight
default:
baseSize = RedeemP2PKHInputSize
}
return baseSize +
(witnessWeight+blockchain.WitnessScaleFactor-1)/
blockchain.WitnessScaleFactor
}