From fe71c905e50041b34177b773810a5e3334ed4061 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 17 Nov 2020 13:23:00 +0100 Subject: [PATCH 1/4] input/size: split constants into confirmed/non-confirmed This to more easily track mismatches if constants and get more accurate fee estimates for the two channel types. The non-anchor weight estimates will now be smaller, this is okay since these constants are only being used for fee estimation (and will now be more accurate). --- input/size.go | 71 ++++++++++++++++++++++++--------------- input/size_test.go | 24 ++++++------- sweep/txgenerator_test.go | 2 +- 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/input/size.go b/input/size.go index b1ddfe948a9..50a24206448 100644 --- a/input/size.go +++ b/input/size.go @@ -286,7 +286,7 @@ const ( // - witness_script (to_remote_delayed_script) ToRemoteConfirmedWitnessSize = 1 + 1 + 73 + 1 + ToRemoteConfirmedScriptSize - // AcceptedHtlcScriptSize 143 bytes + // AcceptedHtlcScriptSize 140 bytes // - OP_DUP: 1 byte // - OP_HASH160: 1 byte // - OP_DATA: 1 byte (RIPEMD160(SHA256(revocationkey)) length) @@ -321,20 +321,18 @@ const ( // - OP_DROP: 1 byte // - OP_CHECKSIG: 1 byte // - OP_ENDIF: 1 byte - // - OP_1: 1 byte // These 3 extra bytes are used for both confirmed and regular - // - OP_CSV: 1 byte // HTLC script types. The size won't be correct in all cases, - // - OP_DROP: 1 byte // but it is just an upper bound used for fee estimation in any case. + // - OP_1: 1 byte // These 3 extra bytes are only + // - OP_CSV: 1 byte // present for the confirmed + // - OP_DROP: 1 byte // HTLC script types. // - OP_ENDIF: 1 byte AcceptedHtlcScriptSize = 3*1 + 20 + 5*1 + 33 + 8*1 + 20 + 4*1 + - 33 + 5*1 + 4 + 8*1 + 33 + 5*1 + 4 + 5*1 // AcceptedHtlcScriptSizeConfirmed 143 bytes - // - // TODO(halseth): the non-confirmed version currently includes the - // overhead. - AcceptedHtlcScriptSizeConfirmed = AcceptedHtlcScriptSize // + HtlcConfirmedScriptOverhead + AcceptedHtlcScriptSizeConfirmed = AcceptedHtlcScriptSize + + HtlcConfirmedScriptOverhead - // AcceptedHtlcTimeoutWitnessSize 219 + // AcceptedHtlcTimeoutWitnessSize 216 // - number_of_witness_elements: 1 byte // - sender_sig_length: 1 byte // - sender_sig: 73 bytes @@ -343,7 +341,11 @@ const ( // - witness_script: (accepted_htlc_script) AcceptedHtlcTimeoutWitnessSize = 1 + 1 + 73 + 1 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcPenaltyWitnessSize 252 bytes + // AcceptedHtlcTimeoutWitnessSizeConfirmed 219 bytes + AcceptedHtlcTimeoutWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 1 + + AcceptedHtlcScriptSizeConfirmed + + // AcceptedHtlcPenaltyWitnessSize 249 bytes // - number_of_witness_elements: 1 byte // - revocation_sig_length: 1 byte // - revocation_sig: 73 bytes @@ -353,7 +355,11 @@ const ( // - witness_script (accepted_htlc_script) AcceptedHtlcPenaltyWitnessSize = 1 + 1 + 73 + 1 + 33 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcSuccessWitnessSize 322 bytes + // AcceptedHtlcPenaltyWitnessSizeConfirmed 252 bytes + AcceptedHtlcPenaltyWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 33 + 1 + + AcceptedHtlcScriptSizeConfirmed + + // AcceptedHtlcSuccessWitnessSize 319 bytes // - number_of_witness_elements: 1 byte // - nil_length: 1 byte // - sig_alice_length: 1 byte @@ -364,16 +370,18 @@ const ( // - preimage: 32 bytes // - witness_script_length: 1 byte // - witness_script (accepted_htlc_script) + // + // Input to second level success tx, spending non-delayed HTLC output. AcceptedHtlcSuccessWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcSuccessWitnessSizeConfirmed 327 bytes + // AcceptedHtlcSuccessWitnessSizeConfirmed 322 bytes // // Input to second level success tx, spending 1 CSV delayed HTLC output. AcceptedHtlcSuccessWitnessSizeConfirmed = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + AcceptedHtlcScriptSizeConfirmed - // OfferedHtlcScriptSize 136 bytes + // OfferedHtlcScriptSize 133 bytes // - OP_DUP: 1 byte // - OP_HASH160: 1 byte // - OP_DATA: 1 byte (RIPEMD160(SHA256(revocationkey)) length) @@ -404,19 +412,17 @@ const ( // - OP_EQUALVERIFY: 1 byte // - OP_CHECKSIG: 1 byte // - OP_ENDIF: 1 byte - // - OP_1: 1 byte - // - OP_CSV: 1 byte - // - OP_DROP: 1 byte + // - OP_1: 1 byte // These 3 extra bytes are only + // - OP_CSV: 1 byte // present for the confirmed + // - OP_DROP: 1 byte // HTLC script types. // - OP_ENDIF: 1 byte - OfferedHtlcScriptSize = 3*1 + 20 + 5*1 + 33 + 10*1 + 33 + 5*1 + 20 + 7*1 + OfferedHtlcScriptSize = 3*1 + 20 + 5*1 + 33 + 10*1 + 33 + 5*1 + 20 + 4*1 // OfferedHtlcScriptSizeConfirmed 136 bytes - // - // TODO(halseth): the non-confirmed version currently includes the - // overhead. - OfferedHtlcScriptSizeConfirmed = OfferedHtlcScriptSize // + HtlcConfirmedScriptOverhead + OfferedHtlcScriptSizeConfirmed = OfferedHtlcScriptSize + + HtlcConfirmedScriptOverhead - // OfferedHtlcSuccessWitnessSize 245 bytes + // OfferedHtlcSuccessWitnessSize 242 bytes // - number_of_witness_elements: 1 byte // - receiver_sig_length: 1 byte // - receiver_sig: 73 bytes @@ -426,7 +432,11 @@ const ( // - witness_script (offered_htlc_script) OfferedHtlcSuccessWitnessSize = 1 + 1 + 73 + 1 + 32 + 1 + OfferedHtlcScriptSize - // OfferedHtlcTimeoutWitnessSize 285 bytes + // OfferedHtlcSuccessWitnessSizeConfirmed 245 bytes + OfferedHtlcSuccessWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 32 + 1 + + OfferedHtlcScriptSizeConfirmed + + // OfferedHtlcTimeoutWitnessSize 282 bytes // - number_of_witness_elements: 1 byte // - nil_length: 1 byte // - sig_alice_length: 1 byte @@ -436,15 +446,18 @@ const ( // - nil_length: 1 byte // - witness_script_length: 1 byte // - witness_script (offered_htlc_script) - OfferedHtlcTimeoutWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + OfferedHtlcScriptSize + // + // Input to second level timeout tx, spending non-delayed HTLC output. + OfferedHtlcTimeoutWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + + OfferedHtlcScriptSize - // OfferedHtlcTimeoutWitnessSizeConfirmed 288 bytes + // OfferedHtlcTimeoutWitnessSizeConfirmed 285 bytes // // Input to second level timeout tx, spending 1 CSV delayed HTLC output. OfferedHtlcTimeoutWitnessSizeConfirmed = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + OfferedHtlcScriptSizeConfirmed - // OfferedHtlcPenaltyWitnessSize 246 bytes + // OfferedHtlcPenaltyWitnessSize 243 bytes // - number_of_witness_elements: 1 byte // - revocation_sig_length: 1 byte // - revocation_sig: 73 bytes @@ -454,6 +467,10 @@ const ( // - witness_script (offered_htlc_script) OfferedHtlcPenaltyWitnessSize = 1 + 1 + 73 + 1 + 33 + 1 + OfferedHtlcScriptSize + // OfferedHtlcPenaltyWitnessSizeConfirmed 246 bytes + OfferedHtlcPenaltyWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 33 + 1 + + OfferedHtlcScriptSizeConfirmed + // AnchorScriptSize 40 bytes // - pubkey_length: 1 byte // - pubkey: 33 bytes diff --git a/input/size_test.go b/input/size_test.go index c7c2dc1b3a6..2b397395ae3 100644 --- a/input/size_test.go +++ b/input/size_test.go @@ -485,7 +485,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc revoke", - expSize: input.OfferedHtlcPenaltyWitnessSize - 3, + expSize: input.OfferedHtlcPenaltyWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.SenderHTLCScript( testPubkey, testPubkey, testPubkey, @@ -515,7 +515,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc revoke confirmed", - expSize: input.OfferedHtlcPenaltyWitnessSize, + expSize: input.OfferedHtlcPenaltyWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { hash := make([]byte, 20) @@ -547,7 +547,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc timeout", - expSize: input.OfferedHtlcTimeoutWitnessSize - 3, + expSize: input.OfferedHtlcTimeoutWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.SenderHTLCScript( testPubkey, testPubkey, testPubkey, @@ -574,7 +574,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc timeout confirmed", - expSize: input.OfferedHtlcTimeoutWitnessSize, + expSize: input.OfferedHtlcTimeoutWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.SenderHTLCScript( testPubkey, testPubkey, testPubkey, @@ -601,7 +601,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc success", - expSize: input.OfferedHtlcSuccessWitnessSize - 3, + expSize: input.OfferedHtlcSuccessWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.SenderHTLCScript( testPubkey, testPubkey, testPubkey, @@ -627,7 +627,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "offered htlc success confirmed", - expSize: input.OfferedHtlcSuccessWitnessSize, + expSize: input.OfferedHtlcSuccessWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.SenderHTLCScript( testPubkey, testPubkey, testPubkey, @@ -653,7 +653,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc revoke", - expSize: input.AcceptedHtlcPenaltyWitnessSize - 3, + expSize: input.AcceptedHtlcPenaltyWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( testCLTVExpiry, testPubkey, testPubkey, @@ -683,7 +683,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc revoke confirmed", - expSize: input.AcceptedHtlcPenaltyWitnessSize, + expSize: input.AcceptedHtlcPenaltyWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( testCLTVExpiry, testPubkey, testPubkey, @@ -713,7 +713,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc timeout", - expSize: input.AcceptedHtlcTimeoutWitnessSize - 3, + expSize: input.AcceptedHtlcTimeoutWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( @@ -741,7 +741,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc timeout confirmed", - expSize: input.AcceptedHtlcTimeoutWitnessSize, + expSize: input.AcceptedHtlcTimeoutWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( testCLTVExpiry, testPubkey, testPubkey, @@ -768,7 +768,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc success", - expSize: input.AcceptedHtlcSuccessWitnessSize - 3, + expSize: input.AcceptedHtlcSuccessWitnessSize, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( testCLTVExpiry, testPubkey, testPubkey, @@ -798,7 +798,7 @@ var witnessSizeTests = []witnessSizeTest{ }, { name: "accepted htlc success confirmed", - expSize: input.AcceptedHtlcSuccessWitnessSize, + expSize: input.AcceptedHtlcSuccessWitnessSizeConfirmed, genWitness: func(t *testing.T) wire.TxWitness { witScript, err := input.ReceiverHTLCScript( testCLTVExpiry, testPubkey, testPubkey, diff --git a/sweep/txgenerator_test.go b/sweep/txgenerator_test.go index 0543543df00..f8a6b8ac4f0 100644 --- a/sweep/txgenerator_test.go +++ b/sweep/txgenerator_test.go @@ -15,7 +15,7 @@ var ( input.HtlcOfferedRemoteTimeout, input.WitnessKeyHash, } - expectedWeight = int64(1463) + expectedWeight = int64(1460) expectedSummary = "0000000000000000000000000000000000000000000000000000000000000000:10 (CommitmentTimeLock), " + "0000000000000000000000000000000000000000000000000000000000000001:11 (HtlcAcceptedSuccessSecondLevel), " + "0000000000000000000000000000000000000000000000000000000000000002:12 (HtlcOfferedRemoteTimeout), " + From 3a3076397a166b93a343cf34f3c092040d34342c Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 17 Nov 2020 15:24:56 +0100 Subject: [PATCH 2/4] input/size: fix comments for constants We run a script that ensures the constant sizes listed is actually the value of the constant. --- input/size.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/input/size.go b/input/size.go index 50a24206448..6ad55d87a5c 100644 --- a/input/size.go +++ b/input/size.go @@ -115,6 +115,7 @@ const ( // - Sequence: 4 bytes InputSize = 32 + 4 + 1 + 4 + // FundingInputSize 41 bytes // FundingInputSize represents the size of an input to a funding // transaction, and is equivalent to the size of a standard segwit input // as calculated above. @@ -204,31 +205,37 @@ const ( // HTLCWeight 172 weight HTLCWeight = witnessScaleFactor * HTLCSize + // HtlcTimeoutWeight 663 weight // HtlcTimeoutWeight is the weight of the HTLC timeout transaction // which will transition an outgoing HTLC to the delay-and-claim state. HtlcTimeoutWeight = 663 + // HtlcSuccessWeight 703 weight // HtlcSuccessWeight is the weight of the HTLC success transaction // which will transition an incoming HTLC to the delay-and-claim state. HtlcSuccessWeight = 703 + // HtlcConfirmedScriptOverhead 3 bytes // HtlcConfirmedScriptOverhead is the extra length of an HTLC script // that requires confirmation before it can be spent. These extra bytes // is a result of the extra CSV check. HtlcConfirmedScriptOverhead = 3 + // HtlcTimeoutWeightConfirmed 666 weight // HtlcTimeoutWeightConfirmed is the weight of the HTLC timeout // transaction which will transition an outgoing HTLC to the // delay-and-claim state, for the confirmed HTLC outputs. It is 3 bytes // larger because of the additional CSV check in the input script. HtlcTimeoutWeightConfirmed = HtlcTimeoutWeight + HtlcConfirmedScriptOverhead - // HtlcSuccessWeightCOnfirmed is the weight of the HTLC success + // HtlcSuccessWeightConfirmed 706 weight + // HtlcSuccessWeightConfirmed is the weight of the HTLC success // transaction which will transition an incoming HTLC to the // delay-and-claim state, for the confirmed HTLC outputs. It is 3 bytes // larger because of the cdditional CSV check in the input script. HtlcSuccessWeightConfirmed = HtlcSuccessWeight + HtlcConfirmedScriptOverhead + // MaxHTLCNumber 966 // MaxHTLCNumber is the maximum number HTLCs which can be included in a // commitment transaction. This limit was chosen such that, in the case // of a contract breach, the punishment transaction is able to sweep @@ -332,7 +339,7 @@ const ( AcceptedHtlcScriptSizeConfirmed = AcceptedHtlcScriptSize + HtlcConfirmedScriptOverhead - // AcceptedHtlcTimeoutWitnessSize 216 + // AcceptedHtlcTimeoutWitnessSize 217 bytes // - number_of_witness_elements: 1 byte // - sender_sig_length: 1 byte // - sender_sig: 73 bytes @@ -341,11 +348,11 @@ const ( // - witness_script: (accepted_htlc_script) AcceptedHtlcTimeoutWitnessSize = 1 + 1 + 73 + 1 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcTimeoutWitnessSizeConfirmed 219 bytes + // AcceptedHtlcTimeoutWitnessSizeConfirmed 220 bytes AcceptedHtlcTimeoutWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 1 + AcceptedHtlcScriptSizeConfirmed - // AcceptedHtlcPenaltyWitnessSize 249 bytes + // AcceptedHtlcPenaltyWitnessSize 250 bytes // - number_of_witness_elements: 1 byte // - revocation_sig_length: 1 byte // - revocation_sig: 73 bytes @@ -355,11 +362,11 @@ const ( // - witness_script (accepted_htlc_script) AcceptedHtlcPenaltyWitnessSize = 1 + 1 + 73 + 1 + 33 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcPenaltyWitnessSizeConfirmed 252 bytes + // AcceptedHtlcPenaltyWitnessSizeConfirmed 253 bytes AcceptedHtlcPenaltyWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 33 + 1 + AcceptedHtlcScriptSizeConfirmed - // AcceptedHtlcSuccessWitnessSize 319 bytes + // AcceptedHtlcSuccessWitnessSize 324 bytes // - number_of_witness_elements: 1 byte // - nil_length: 1 byte // - sig_alice_length: 1 byte @@ -375,7 +382,7 @@ const ( AcceptedHtlcSuccessWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + AcceptedHtlcScriptSize - // AcceptedHtlcSuccessWitnessSizeConfirmed 322 bytes + // AcceptedHtlcSuccessWitnessSizeConfirmed 327 bytes // // Input to second level success tx, spending 1 CSV delayed HTLC output. AcceptedHtlcSuccessWitnessSizeConfirmed = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 32 + 1 + @@ -436,7 +443,7 @@ const ( OfferedHtlcSuccessWitnessSizeConfirmed = 1 + 1 + 73 + 1 + 32 + 1 + OfferedHtlcScriptSizeConfirmed - // OfferedHtlcTimeoutWitnessSize 282 bytes + // OfferedHtlcTimeoutWitnessSize 285 bytes // - number_of_witness_elements: 1 byte // - nil_length: 1 byte // - sig_alice_length: 1 byte @@ -451,7 +458,7 @@ const ( OfferedHtlcTimeoutWitnessSize = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + OfferedHtlcScriptSize - // OfferedHtlcTimeoutWitnessSizeConfirmed 285 bytes + // OfferedHtlcTimeoutWitnessSizeConfirmed 288 bytes // // Input to second level timeout tx, spending 1 CSV delayed HTLC output. OfferedHtlcTimeoutWitnessSizeConfirmed = 1 + 1 + 1 + 73 + 1 + 73 + 1 + 1 + From 2bc37db61a85586fbc42777a3c20564b626f7d2b Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 17 Nov 2020 12:50:41 +0100 Subject: [PATCH 3/4] lnwallet: export HTLC generating methods --- lnwallet/channel.go | 12 ++++++------ lnwallet/transactions.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 25f72a37223..792e5c59097 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3022,7 +3022,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, Hash: txHash, Index: uint32(htlc.remoteOutputIndex), } - sigJob.Tx, err = createHtlcTimeoutTx( + sigJob.Tx, err = CreateHtlcTimeoutTx( chanType, op, outputAmt, htlc.Timeout, uint32(remoteChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, @@ -3075,7 +3075,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, Hash: txHash, Index: uint32(htlc.remoteOutputIndex), } - sigJob.Tx, err = createHtlcSuccessTx( + sigJob.Tx, err = CreateHtlcSuccessTx( chanType, op, outputAmt, uint32(remoteChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, ) @@ -4119,7 +4119,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, htlcFee := HtlcSuccessFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee - successTx, err := createHtlcSuccessTx( + successTx, err := CreateHtlcSuccessTx( chanType, op, outputAmt, uint32(localChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, @@ -4173,7 +4173,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, htlcFee := HtlcTimeoutFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee - timeoutTx, err := createHtlcTimeoutTx( + timeoutTx, err := CreateHtlcTimeoutTx( chanType, op, outputAmt, htlc.Timeout, uint32(localChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, @@ -5689,7 +5689,7 @@ func newOutgoingHtlcResolution(signer input.Signer, // With the fee calculated, re-construct the second level timeout // transaction. - timeoutTx, err := createHtlcTimeoutTx( + timeoutTx, err := CreateHtlcTimeoutTx( chanType, op, secondLevelOutputAmt, htlc.RefundTimeout, csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, ) @@ -5827,7 +5827,7 @@ func newIncomingHtlcResolution(signer input.Signer, // taking into account the fee rate used. htlcFee := HtlcSuccessFee(chanType, feePerKw) secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee - successTx, err := createHtlcSuccessTx( + successTx, err := CreateHtlcSuccessTx( chanType, op, secondLevelOutputAmt, csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, ) diff --git a/lnwallet/transactions.go b/lnwallet/transactions.go index 3c37cd8da78..861fdebcea1 100644 --- a/lnwallet/transactions.go +++ b/lnwallet/transactions.go @@ -35,7 +35,7 @@ var ( TimelockShift = uint32(1 << 29) ) -// createHtlcSuccessTx creates a transaction that spends the output on the +// CreateHtlcSuccessTx creates a transaction that spends the output on the // commitment transaction of the peer that receives an HTLC. This transaction // essentially acts as an off-chain covenant as it's only permitted to spend // the designated HTLC output, and also that spend can _only_ be used as a @@ -45,7 +45,7 @@ var ( // In order to spend the HTLC output, the witness for the passed transaction // should be: // * <0> -func createHtlcSuccessTx(chanType channeldb.ChannelType, +func CreateHtlcSuccessTx(chanType channeldb.ChannelType, htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay uint32, revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) { @@ -85,7 +85,7 @@ func createHtlcSuccessTx(chanType channeldb.ChannelType, return successTx, nil } -// createHtlcTimeoutTx creates a transaction that spends the HTLC output on the +// CreateHtlcTimeoutTx creates a transaction that spends the HTLC output on the // commitment transaction of the peer that created an HTLC (the sender). This // transaction essentially acts as an off-chain covenant as it spends a 2-of-2 // multi-sig output. This output requires a signature from both the sender and @@ -101,7 +101,7 @@ func createHtlcSuccessTx(chanType channeldb.ChannelType, // NOTE: The passed amount for the HTLC should take into account the required // fee rate at the time the HTLC was created. The fee should be able to // entirely pay for this (tiny: 1-in 1-out) transaction. -func createHtlcTimeoutTx(chanType channeldb.ChannelType, +func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, cltvExpiry, csvDelay uint32, revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) { From d30aae43e69d682ae86f421d55e8670c7821c3dd Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 1 Feb 2021 14:21:09 +0100 Subject: [PATCH 4/4] input/size: add txSize test 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. --- input/size_test.go | 166 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/input/size_test.go b/input/size_test.go index 2b397395ae3..8008ac854aa 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) + } + }) + } +}