diff --git a/input/size_test.go b/input/size_test.go index 2b397395..8008ac85 100644 --- a/input/size_test.go +++ b/input/size_test.go @@ -7,12 +7,16 @@ import ( "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 ( @@ -23,6 +27,8 @@ const ( // maxDERSignatureSize is the largest possible DER-encoded signature // without the trailing sighash flag. maxDERSignatureSize = 72 + + testAmt = btcutil.MaxSatoshi ) var ( @@ -40,6 +46,11 @@ var ( 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 @@ -845,3 +856,158 @@ func TestWitnessSizes(t *testing.T) { }) } } + +// 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) + } + }) + } +}