mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
d30aae43e6
Similar to what we do for witnesses, check that the HTLC weight constants check out. They actually do not, since the spec is off by one. We ensure we agree with the spec.
1014 lines
23 KiB
Go
1014 lines
23 KiB
Go
package input_test
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
|
"github.com/btcsuite/btcd/btcec"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcutil"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
)
|
|
|
|
const (
|
|
testCSVDelay = (1 << 31) - 1
|
|
|
|
testCLTVExpiry = 500000000
|
|
|
|
// maxDERSignatureSize is the largest possible DER-encoded signature
|
|
// without the trailing sighash flag.
|
|
maxDERSignatureSize = 72
|
|
|
|
testAmt = btcutil.MaxSatoshi
|
|
)
|
|
|
|
var (
|
|
testPubkeyBytes = make([]byte, 33)
|
|
|
|
testHash160 = make([]byte, 20)
|
|
testPreimage = make([]byte, 32)
|
|
|
|
// testPubkey is a pubkey used in script size calculation.
|
|
testPubkey = &btcec.PublicKey{
|
|
X: &big.Int{},
|
|
Y: &big.Int{},
|
|
}
|
|
|
|
testPrivkey, _ = btcec.PrivKeyFromBytes(btcec.S256(), make([]byte, 32))
|
|
|
|
testTx = wire.NewMsgTx(2)
|
|
|
|
testOutPoint = wire.OutPoint{
|
|
Hash: chainhash.Hash{},
|
|
Index: 1,
|
|
}
|
|
)
|
|
|
|
// TestTxWeightEstimator tests that transaction weight estimates are calculated
|
|
// correctly by comparing against an actual (though invalid) transaction
|
|
// matching the template.
|
|
func TestTxWeightEstimator(t *testing.T) {
|
|
netParams := &chaincfg.MainNetParams
|
|
|
|
p2pkhAddr, err := btcutil.NewAddressPubKeyHash(
|
|
make([]byte, 20), netParams)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate address: %v", err)
|
|
}
|
|
p2pkhScript, err := txscript.PayToAddrScript(p2pkhAddr)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate scriptPubKey: %v", err)
|
|
}
|
|
|
|
p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
|
make([]byte, 20), netParams)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate address: %v", err)
|
|
}
|
|
p2wkhScript, err := txscript.PayToAddrScript(p2wkhAddr)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate scriptPubKey: %v", err)
|
|
}
|
|
|
|
p2wshAddr, err := btcutil.NewAddressWitnessScriptHash(
|
|
make([]byte, 32), netParams)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate address: %v", err)
|
|
}
|
|
p2wshScript, err := txscript.PayToAddrScript(p2wshAddr)
|
|
if err != nil {
|
|
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
|
|
numP2WSHInputs int
|
|
numNestedP2WKHInputs int
|
|
numNestedP2WSHInputs int
|
|
numP2PKHOutputs int
|
|
numP2WKHOutputs int
|
|
numP2WSHOutputs int
|
|
numP2SHOutputs int
|
|
}{
|
|
// Assert base txn size.
|
|
{},
|
|
|
|
// Assert single input/output sizes.
|
|
{
|
|
numP2PKHInputs: 1,
|
|
},
|
|
{
|
|
numP2WKHInputs: 1,
|
|
},
|
|
{
|
|
numP2WSHInputs: 1,
|
|
},
|
|
{
|
|
numNestedP2WKHInputs: 1,
|
|
},
|
|
{
|
|
numNestedP2WSHInputs: 1,
|
|
},
|
|
{
|
|
numP2WKHOutputs: 1,
|
|
},
|
|
{
|
|
numP2PKHOutputs: 1,
|
|
},
|
|
{
|
|
numP2WSHOutputs: 1,
|
|
},
|
|
{
|
|
numP2SHOutputs: 1,
|
|
},
|
|
|
|
// Assert each input/output increments input/output counts.
|
|
{
|
|
numP2PKHInputs: 253,
|
|
},
|
|
{
|
|
numP2WKHInputs: 253,
|
|
},
|
|
{
|
|
numP2WSHInputs: 253,
|
|
},
|
|
{
|
|
numNestedP2WKHInputs: 253,
|
|
},
|
|
{
|
|
numNestedP2WSHInputs: 253,
|
|
},
|
|
{
|
|
numP2WKHOutputs: 253,
|
|
},
|
|
{
|
|
numP2PKHOutputs: 253,
|
|
},
|
|
{
|
|
numP2WSHOutputs: 253,
|
|
},
|
|
{
|
|
numP2SHOutputs: 253,
|
|
},
|
|
|
|
// Assert basic combinations of inputs and outputs.
|
|
{
|
|
numP2PKHInputs: 1,
|
|
numP2PKHOutputs: 2,
|
|
},
|
|
{
|
|
numP2PKHInputs: 1,
|
|
numP2WKHInputs: 1,
|
|
numP2WKHOutputs: 1,
|
|
numP2WSHOutputs: 1,
|
|
},
|
|
{
|
|
numP2WKHInputs: 1,
|
|
numP2WKHOutputs: 1,
|
|
numP2WSHOutputs: 1,
|
|
},
|
|
{
|
|
numP2WKHInputs: 2,
|
|
numP2WKHOutputs: 1,
|
|
numP2WSHOutputs: 1,
|
|
},
|
|
{
|
|
numP2WSHInputs: 1,
|
|
numP2WKHOutputs: 1,
|
|
},
|
|
{
|
|
numP2PKHInputs: 1,
|
|
numP2SHOutputs: 1,
|
|
},
|
|
{
|
|
numNestedP2WKHInputs: 1,
|
|
numP2WKHOutputs: 1,
|
|
},
|
|
{
|
|
numNestedP2WSHInputs: 1,
|
|
numP2WKHOutputs: 1,
|
|
},
|
|
|
|
// Assert disparate input/output types increment total
|
|
// input/output counts.
|
|
{
|
|
numP2PKHInputs: 50,
|
|
numP2WKHInputs: 50,
|
|
numP2WSHInputs: 51,
|
|
numNestedP2WKHInputs: 51,
|
|
numNestedP2WSHInputs: 51,
|
|
numP2WKHOutputs: 1,
|
|
},
|
|
{
|
|
numP2WKHInputs: 1,
|
|
numP2WKHOutputs: 63,
|
|
numP2PKHOutputs: 63,
|
|
numP2WSHOutputs: 63,
|
|
numP2SHOutputs: 64,
|
|
},
|
|
{
|
|
numP2PKHInputs: 50,
|
|
numP2WKHInputs: 50,
|
|
numP2WSHInputs: 51,
|
|
numNestedP2WKHInputs: 51,
|
|
numNestedP2WSHInputs: 51,
|
|
numP2WKHOutputs: 63,
|
|
numP2PKHOutputs: 63,
|
|
numP2WSHOutputs: 63,
|
|
numP2SHOutputs: 64,
|
|
},
|
|
}
|
|
|
|
for i, test := range testCases {
|
|
var weightEstimate input.TxWeightEstimator
|
|
tx := wire.NewMsgTx(1)
|
|
|
|
for j := 0; j < test.numP2PKHInputs; j++ {
|
|
weightEstimate.AddP2PKHInput()
|
|
|
|
signature := make([]byte, maxDERSignatureSize+1)
|
|
compressedPubKey := make([]byte, 33)
|
|
scriptSig, err := txscript.NewScriptBuilder().AddData(signature).
|
|
AddData(compressedPubKey).Script()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate scriptSig: %v", err)
|
|
}
|
|
|
|
tx.AddTxIn(&wire.TxIn{SignatureScript: scriptSig})
|
|
}
|
|
for j := 0; j < test.numP2WKHInputs; j++ {
|
|
weightEstimate.AddP2WKHInput()
|
|
|
|
signature := make([]byte, maxDERSignatureSize+1)
|
|
compressedPubKey := make([]byte, 33)
|
|
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, maxDERSignatureSize+1)
|
|
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})
|
|
}
|
|
for j := 0; j < test.numP2WKHOutputs; j++ {
|
|
weightEstimate.AddP2WKHOutput()
|
|
tx.AddTxOut(&wire.TxOut{PkScript: p2wkhScript})
|
|
}
|
|
for j := 0; j < test.numP2WSHOutputs; j++ {
|
|
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) {
|
|
t.Errorf("Case %d: Got wrong weight: expected %d, got %d",
|
|
i, expectedWeight, weightEstimate.Weight())
|
|
}
|
|
}
|
|
}
|
|
|
|
type maxDERSignature struct{}
|
|
|
|
func (s *maxDERSignature) Serialize() []byte {
|
|
// Always return worst-case signature length, excluding the one byte
|
|
// sighash flag.
|
|
return make([]byte, maxDERSignatureSize)
|
|
}
|
|
|
|
func (s *maxDERSignature) Verify(_ []byte, _ *btcec.PublicKey) bool {
|
|
return true
|
|
}
|
|
|
|
// dummySigner is a fake signer used for size (upper bound) calculations.
|
|
type dummySigner struct {
|
|
input.Signer
|
|
}
|
|
|
|
// SignOutputRaw generates a signature for the passed transaction according to
|
|
// the data within the passed SignDescriptor.
|
|
func (s *dummySigner) SignOutputRaw(tx *wire.MsgTx,
|
|
signDesc *input.SignDescriptor) (input.Signature, error) {
|
|
|
|
return &maxDERSignature{}, nil
|
|
}
|
|
|
|
type witnessSizeTest struct {
|
|
name string
|
|
expSize int
|
|
genWitness func(t *testing.T) wire.TxWitness
|
|
}
|
|
|
|
var witnessSizeTests = []witnessSizeTest{
|
|
{
|
|
name: "funding",
|
|
expSize: input.MultiSigWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witnessScript, _, err := input.GenFundingPkScript(
|
|
testPubkeyBytes, testPubkeyBytes, 1,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return input.SpendMultiSig(
|
|
witnessScript,
|
|
testPubkeyBytes, &maxDERSignature{},
|
|
testPubkeyBytes, &maxDERSignature{},
|
|
)
|
|
},
|
|
},
|
|
{
|
|
name: "to local timeout",
|
|
expSize: input.ToLocalTimeoutWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witnessScript, err := input.CommitScriptToSelf(
|
|
testCSVDelay, testPubkey, testPubkey,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witnessScript,
|
|
}
|
|
|
|
witness, err := input.CommitSpendTimeout(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "to local revoke",
|
|
expSize: input.ToLocalPenaltyWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witnessScript, err := input.CommitScriptToSelf(
|
|
testCSVDelay, testPubkey, testPubkey,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witnessScript,
|
|
}
|
|
|
|
witness, err := input.CommitSpendRevoke(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "to remote confirmed",
|
|
expSize: input.ToRemoteConfirmedWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.CommitScriptToRemoteConfirmed(
|
|
testPubkey,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
witness, err := input.CommitSpendToRemoteConfirmed(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "anchor",
|
|
expSize: input.AnchorWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.CommitScriptAnchor(
|
|
testPubkey,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
witness, err := input.CommitSpendAnchor(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "anchor anyone",
|
|
expSize: 43,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.CommitScriptAnchor(
|
|
testPubkey,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
witness, _ := input.CommitSpendAnchorAnyone(witScript)
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc revoke",
|
|
expSize: input.OfferedHtlcPenaltyWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
DoubleTweak: testPrivkey,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendRevoke(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc revoke confirmed",
|
|
expSize: input.OfferedHtlcPenaltyWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
hash := make([]byte, 20)
|
|
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
hash, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
DoubleTweak: testPrivkey,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendRevoke(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc timeout",
|
|
expSize: input.OfferedHtlcTimeoutWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendTimeout(
|
|
&maxDERSignature{}, txscript.SigHashAll,
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc timeout confirmed",
|
|
expSize: input.OfferedHtlcTimeoutWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
testHash160, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendTimeout(
|
|
&maxDERSignature{}, txscript.SigHashAll,
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc success",
|
|
expSize: input.OfferedHtlcSuccessWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendRedeem(
|
|
&dummySigner{}, signDesc, testTx, testPreimage,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "offered htlc success confirmed",
|
|
expSize: input.OfferedHtlcSuccessWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey,
|
|
testHash160, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.SenderHtlcSpendRedeem(
|
|
&dummySigner{}, signDesc, testTx, testPreimage,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc revoke",
|
|
expSize: input.AcceptedHtlcPenaltyWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
DoubleTweak: testPrivkey,
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendRevoke(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc revoke confirmed",
|
|
expSize: input.AcceptedHtlcPenaltyWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
DoubleTweak: testPrivkey,
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendRevoke(
|
|
&dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc timeout",
|
|
expSize: input.AcceptedHtlcTimeoutWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendTimeout(
|
|
&dummySigner{}, signDesc, testTx,
|
|
testCLTVExpiry,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc timeout confirmed",
|
|
expSize: input.AcceptedHtlcTimeoutWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendTimeout(
|
|
&dummySigner{}, signDesc, testTx,
|
|
testCLTVExpiry,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc success",
|
|
expSize: input.AcceptedHtlcSuccessWitnessSize,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, false,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendRedeem(
|
|
&maxDERSignature{}, txscript.SigHashAll,
|
|
testPreimage, &dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
{
|
|
name: "accepted htlc success confirmed",
|
|
expSize: input.AcceptedHtlcSuccessWitnessSizeConfirmed,
|
|
genWitness: func(t *testing.T) wire.TxWitness {
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, true,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
witness, err := input.ReceiverHtlcSpendRedeem(
|
|
&maxDERSignature{}, txscript.SigHashAll,
|
|
testPreimage, &dummySigner{}, signDesc, testTx,
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return witness
|
|
},
|
|
},
|
|
}
|
|
|
|
// TestWitnessSizes asserts the correctness of our magic witness constants.
|
|
// Witnesses involving signatures will have maxDERSignatures injected so that we
|
|
// can determine upper bounds for the witness sizes. These constants are
|
|
// predominately used for fee estimation, so we want to be certain that we
|
|
// aren't under estimating or our transactions could get stuck.
|
|
func TestWitnessSizes(t *testing.T) {
|
|
for _, test := range witnessSizeTests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
size := test.genWitness(t).SerializeSize()
|
|
if size != test.expSize {
|
|
t.Fatalf("size mismatch, want: %v, got: %v",
|
|
test.expSize, size)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// genTimeoutTx creates a signed HTLC second level timeout tx.
|
|
func genTimeoutTx(chanType channeldb.ChannelType) (*wire.MsgTx, error) {
|
|
// Create the unsigned timeout tx.
|
|
timeoutTx, err := lnwallet.CreateHtlcTimeoutTx(
|
|
chanType, testOutPoint, testAmt, testCLTVExpiry,
|
|
testCSVDelay, testPubkey, testPubkey,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// In order to sign the transcation, generate the script for the output
|
|
// it spends.
|
|
witScript, err := input.SenderHTLCScript(
|
|
testPubkey, testPubkey, testPubkey, testHash160,
|
|
chanType.HasAnchors(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
// Sign the timeout tx and add the witness.
|
|
sigHashType := lnwallet.HtlcSigHashType(chanType)
|
|
timeoutWitness, err := input.SenderHtlcSpendTimeout(
|
|
&maxDERSignature{}, sigHashType, &dummySigner{},
|
|
signDesc, timeoutTx,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
timeoutTx.TxIn[0].Witness = timeoutWitness
|
|
|
|
return timeoutTx, nil
|
|
}
|
|
|
|
// genSuccessTx creates a signed HTLC second level success tx.
|
|
func genSuccessTx(chanType channeldb.ChannelType) (*wire.MsgTx, error) {
|
|
// Create the unisgned success tx.
|
|
successTx, err := lnwallet.CreateHtlcSuccessTx(
|
|
chanType, testOutPoint, testAmt, testCSVDelay,
|
|
testPubkey, testPubkey,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// In order to sign the transcation, generate the script for the output
|
|
// it spends.
|
|
witScript, err := input.ReceiverHTLCScript(
|
|
testCLTVExpiry, testPubkey, testPubkey,
|
|
testPubkey, testHash160, chanType.HasAnchors(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
signDesc := &input.SignDescriptor{
|
|
WitnessScript: witScript,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: testPubkey,
|
|
},
|
|
}
|
|
|
|
// Sign the success tx and add the witness.
|
|
sigHashType := lnwallet.HtlcSigHashType(channeldb.SingleFunderBit)
|
|
successWitness, err := input.ReceiverHtlcSpendRedeem(
|
|
&maxDERSignature{}, sigHashType, testPreimage,
|
|
&dummySigner{}, signDesc, successTx,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
successTx.TxIn[0].Witness = successWitness
|
|
|
|
return successTx, nil
|
|
|
|
}
|
|
|
|
type txSizeTest struct {
|
|
name string
|
|
expWeight int64
|
|
genTx func(t *testing.T) *wire.MsgTx
|
|
}
|
|
|
|
var txSizeTests = []txSizeTest{
|
|
{
|
|
name: "htlc timeout regular ",
|
|
expWeight: input.HtlcTimeoutWeight,
|
|
genTx: func(t *testing.T) *wire.MsgTx {
|
|
tx, err := genTimeoutTx(channeldb.SingleFunderBit)
|
|
require.NoError(t, err)
|
|
|
|
return tx
|
|
},
|
|
},
|
|
{
|
|
name: "htlc timeout confirmed",
|
|
expWeight: input.HtlcTimeoutWeightConfirmed,
|
|
genTx: func(t *testing.T) *wire.MsgTx {
|
|
tx, err := genTimeoutTx(channeldb.AnchorOutputsBit)
|
|
require.NoError(t, err)
|
|
|
|
return tx
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "htlc success regular",
|
|
// The weight estimate from the spec is off by one, but it's
|
|
// okay since we overestimate the weight.
|
|
expWeight: input.HtlcSuccessWeight - 1,
|
|
genTx: func(t *testing.T) *wire.MsgTx {
|
|
tx, err := genSuccessTx(channeldb.SingleFunderBit)
|
|
require.NoError(t, err)
|
|
|
|
return tx
|
|
},
|
|
},
|
|
{
|
|
name: "htlc success confirmed",
|
|
// The weight estimate from the spec is off by one, but it's
|
|
// okay since we overestimate the weight.
|
|
expWeight: input.HtlcSuccessWeightConfirmed - 1,
|
|
genTx: func(t *testing.T) *wire.MsgTx {
|
|
tx, err := genSuccessTx(channeldb.AnchorOutputsBit)
|
|
require.NoError(t, err)
|
|
|
|
return tx
|
|
},
|
|
},
|
|
}
|
|
|
|
// TestWitnessSizes asserts the correctness of our magic tx size constants.
|
|
func TestTxSizes(t *testing.T) {
|
|
for _, test := range txSizeTests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
tx := test.genTx(t)
|
|
|
|
weight := blockchain.GetTransactionWeight(btcutil.NewTx(tx))
|
|
if weight != test.expWeight {
|
|
t.Fatalf("size mismatch, want: %v, got: %v",
|
|
test.expWeight, weight)
|
|
}
|
|
})
|
|
}
|
|
}
|