From 3232fd71c2656869834019ec947a36376fcb08f0 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Mon, 2 Oct 2017 19:57:22 -0700 Subject: [PATCH] lnwallet: Add TxWeightEstimator support for nested pay-to-witness. --- lnwallet/size.go | 59 +++++++++++++++++++++++++-------- lnwallet/size_test.go | 77 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/lnwallet/size.go b/lnwallet/size.go index ee755143..6061a141 100644 --- a/lnwallet/size.go +++ b/lnwallet/size.go @@ -19,6 +19,12 @@ const ( // WitnessSize - witness size (bytes). // Weight - the metric for determining the weight of the transaction. + // P2WPKHSize 22 bytes + // - OP_0: 1 byte + // - OP_DATA: 1 byte (PublicKeyHASH160 length) + // - PublicKeyHASH160: 20 bytes + P2WPKHSize = 1 + 1 + 20 + // P2WSHSize 34 bytes // - OP_0: 1 byte // - OP_DATA: 1 byte (WitnessScriptSHA256 length) @@ -35,19 +41,19 @@ const ( // - value: 8 bytes // - var_int: 1 byte (pkscript_length) // - pkscript (p2wpkh): 22 bytes - P2WKHOutputSize = 8 + 1 + 22 + P2WKHOutputSize = 8 + 1 + P2WPKHSize // P2WSHOutputSize 43 bytes // - value: 8 bytes // - var_int: 1 byte (pkscript_length) // - pkscript (p2wsh): 34 bytes - P2WSHOutputSize = 8 + 1 + 34 + P2WSHOutputSize = 8 + 1 + P2WSHSize - // P2WPKHSize 22 bytes - // - OP_0: 1 byte - // - OP_DATA: 1 byte (PublicKeyHASH160 length) - // - PublicKeyHASH160: 20 bytes - P2WPKHSize = 1 + 1 + 20 + // P2SHOutputSize 32 bytes + // - value: 8 bytes + // - var_int: 1 byte (pkscript_length) + // - pkscript (p2sh): 23 bytes + P2SHOutputSize = 8 + 1 + 23 // P2PKHScriptSigSize 108 bytes // - OP_DATA: 1 byte (signature length) @@ -337,14 +343,14 @@ func (twe *TxWeightEstimator) AddP2PKHInput() { } // AddP2WKHInput updates the weight estimate to account for an additional input -// spending a P2PWKH output. +// spending a native P2PWKH output. func (twe *TxWeightEstimator) AddP2WKHInput() { twe.AddWitnessInput(P2WKHWitnessSize) } // AddWitnessInput updates the weight estimate to account for an additional -// input spending a pay-to-witness output. This accepts the total size of the -// witness as a parameter. +// input spending a native pay-to-witness output. This accepts the total size +// of the witness as a parameter. func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) { twe.inputSize += InputSize twe.inputWitnessSize += witnessSize @@ -352,6 +358,24 @@ func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) { twe.hasWitness = true } +// AddNestedP2WKHInput updates the weight estimate to account for an additional +// input spending a P2SH output with a nested P2WKH redeem script. +func (twe *TxWeightEstimator) AddNestedP2WKHInput() { + twe.inputSize += InputSize + P2WPKHSize + twe.inputWitnessSize += P2WKHWitnessSize + twe.inputSize++ + twe.hasWitness = true +} + +// AddNestedP2WSHInput updates the weight estimate to account for an additional +// input spending a P2SH output with a nested P2WSH redeem script. +func (twe *TxWeightEstimator) AddNestedP2WSHInput(witnessSize int) { + twe.inputSize += InputSize + P2WSHSize + twe.inputWitnessSize += witnessSize + twe.inputSize++ + twe.hasWitness = true +} + // AddP2PKHOutput updates the weight estimate to account for an additional P2PKH // output. func (twe *TxWeightEstimator) AddP2PKHOutput() { @@ -359,20 +383,27 @@ func (twe *TxWeightEstimator) AddP2PKHOutput() { twe.outputCount++ } -// AddP2WKHOutput updates the weight estimate to account for an additional P2WKH -// output. +// AddP2WKHOutput updates the weight estimate to account for an additional +// native P2WKH output. func (twe *TxWeightEstimator) AddP2WKHOutput() { twe.outputSize += P2WKHOutputSize twe.outputCount++ } -// AddP2WSHOutput updates the weight estimate to account for an additional P2WSH -// output. +// AddP2WSHOutput updates the weight estimate to account for an additional +// native P2WSH output. func (twe *TxWeightEstimator) AddP2WSHOutput() { twe.outputSize += P2WSHOutputSize twe.outputCount++ } +// AddP2SHOutput updates the weight estimate to account for an additional P2SH +// output. +func (twe *TxWeightEstimator) AddP2SHOutput() { + twe.outputSize += P2SHOutputSize + twe.outputCount++ +} + // Weight gets the estimated weight of the transaction. func (twe *TxWeightEstimator) Weight() int { txSizeStripped := BaseTxSize + diff --git a/lnwallet/size_test.go b/lnwallet/size_test.go index 95296fdd..1c5eef98 100644 --- a/lnwallet/size_test.go +++ b/lnwallet/size_test.go @@ -47,12 +47,25 @@ func TestTxWeightEstimator(t *testing.T) { t.Fatalf("Failed to generate scriptPubKey: %v", err) } + p2shAddr, err := btcutil.NewAddressScriptHash([]byte{0}, netParams) + if err != nil { + t.Fatalf("Failed to generate address: %v", err) + } + p2shScript, err := txscript.PayToAddrScript(p2shAddr) + if err != nil { + t.Fatalf("Failed to generate scriptPubKey: %v", err) + } + testCases := []struct { - numP2PKHInputs int - numP2WKHInputs int - numP2PKHOutputs int - numP2WKHOutputs int - numP2WSHOutputs int + numP2PKHInputs int + numP2WKHInputs int + numP2WSHInputs int + numNestedP2WKHInputs int + numNestedP2WSHInputs int + numP2PKHOutputs int + numP2WKHOutputs int + numP2WSHOutputs int + numP2SHOutputs int }{ { numP2PKHInputs: 1, @@ -74,6 +87,22 @@ func TestTxWeightEstimator(t *testing.T) { numP2WKHOutputs: 1, numP2WSHOutputs: 1, }, + { + numP2WSHInputs: 1, + numP2WKHOutputs: 1, + }, + { + numP2PKHInputs: 1, + numP2SHOutputs: 1, + }, + { + numNestedP2WKHInputs: 1, + numP2WKHOutputs: 1, + }, + { + numNestedP2WSHInputs: 1, + numP2WKHOutputs: 1, + }, } for i, test := range testCases { @@ -101,6 +130,40 @@ func TestTxWeightEstimator(t *testing.T) { witness := wire.TxWitness{signature, compressedPubKey} tx.AddTxIn(&wire.TxIn{Witness: witness}) } + for j := 0; j < test.numP2WSHInputs; j++ { + weightEstimate.AddWitnessInput(42) + + witnessScript := make([]byte, 40) + witness := wire.TxWitness{witnessScript} + tx.AddTxIn(&wire.TxIn{Witness: witness}) + } + for j := 0; j < test.numNestedP2WKHInputs; j++ { + weightEstimate.AddNestedP2WKHInput() + + signature := make([]byte, 73) + compressedPubKey := make([]byte, 33) + witness := wire.TxWitness{signature, compressedPubKey} + scriptSig, err := txscript.NewScriptBuilder().AddData(p2wkhScript). + Script() + if err != nil { + t.Fatalf("Failed to generate scriptSig: %v", err) + } + + tx.AddTxIn(&wire.TxIn{SignatureScript: scriptSig, Witness: witness}) + } + for j := 0; j < test.numNestedP2WSHInputs; j++ { + weightEstimate.AddNestedP2WSHInput(42) + + witnessScript := make([]byte, 40) + witness := wire.TxWitness{witnessScript} + scriptSig, err := txscript.NewScriptBuilder().AddData(p2wshScript). + Script() + if err != nil { + t.Fatalf("Failed to generate scriptSig: %v", err) + } + + tx.AddTxIn(&wire.TxIn{SignatureScript: scriptSig, Witness: witness}) + } for j := 0; j < test.numP2PKHOutputs; j++ { weightEstimate.AddP2PKHOutput() tx.AddTxOut(&wire.TxOut{PkScript: p2pkhScript}) @@ -113,6 +176,10 @@ func TestTxWeightEstimator(t *testing.T) { weightEstimate.AddP2WSHOutput() tx.AddTxOut(&wire.TxOut{PkScript: p2wshScript}) } + for j := 0; j < test.numP2SHOutputs; j++ { + weightEstimate.AddP2SHOutput() + tx.AddTxOut(&wire.TxOut{PkScript: p2shScript}) + } expectedWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) if weightEstimate.Weight() != int(expectedWeight) {