diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index 157d6c86b..65622d315 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -573,17 +573,24 @@ func ReceiverHtlcSpendRevoke(signer Signer, signDesc *SignDescriptor, // receiverHtlcSpendTimeout constructs a valid witness allowing the sender of // an HTLC to recover the pending funds after an absolute timeout in the // scenario that the receiver of the HTLC broadcasts their version of the -// commitment transaction. +// commitment transaction. If the caller has already set the lock time on the +// spending transaction, than a value of -1 can be passed for the cltvExipiry +// value. // // NOTE: The target input of the passed transaction MUST NOT have a final // sequence number. Otherwise, the OP_CHECKLOCKTIMEVERIFY check will fail. func receiverHtlcSpendTimeout(signer Signer, signDesc *SignDescriptor, - sweepTx *wire.MsgTx, cltvExpiry uint32) (wire.TxWitness, error) { + sweepTx *wire.MsgTx, cltvExpiry int32) (wire.TxWitness, error) { - // The HTLC output has an absolute time period before we are permitted - // to recover the pending funds. Therefore we need to set the locktime - // on this sweeping transaction in order to pass Script verification. - sweepTx.LockTime = cltvExpiry + // If the caller set a proper timeout value, then we'll apply it + // directly to the transaction. + if cltvExpiry != -1 { + // The HTLC output has an absolute time period before we are + // permitted to recover the pending funds. Therefore we need to + // set the locktime on this sweeping transaction in order to + // pass Script verification. + sweepTx.LockTime = uint32(cltvExpiry) + } // With the lock time on the transaction set, we'll not generate a // signature for the sweep transaction. The passed sign descriptor diff --git a/lnwallet/signdescriptor.go b/lnwallet/signdescriptor.go index cba39ec22..f2057d2ad 100644 --- a/lnwallet/signdescriptor.go +++ b/lnwallet/signdescriptor.go @@ -78,6 +78,7 @@ type SignDescriptor struct { // WriteSignDescriptor serializes a SignDescriptor struct into the passed // io.Writer stream. +// // NOTE: We assume the SigHashes and InputIndex fields haven't been assigned // yet, since that is usually done just before broadcast by the witness // generator. @@ -119,7 +120,6 @@ func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error { // ReadSignDescriptor deserializes a SignDescriptor struct from the passed // io.Reader stream. func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error { - pubKeyBytes, err := wire.ReadVarBytes(r, 0, 34, "pubkey") if err != nil { return err @@ -134,11 +134,11 @@ func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error { return err } - // Serializing a SignDescriptor with a nil-valued SingleTweak results in - // deserializing a zero-length slice. Since a nil-valued SingleTweak has - // special meaning and a zero-length slice for a SingleTweak is invalid, - // we can use the zero-length slice as the flag for a nil-valued - // SingleTweak. + // Serializing a SignDescriptor with a nil-valued SingleTweak results + // in deserializing a zero-length slice. Since a nil-valued SingleTweak + // has special meaning and a zero-length slice for a SingleTweak is + // invalid, we can use the zero-length slice as the flag for a + // nil-valued SingleTweak. if len(singleTweak) == 0 { sd.SingleTweak = nil } else { @@ -150,11 +150,11 @@ func ReadSignDescriptor(r io.Reader, sd *SignDescriptor) error { return err } - // Serializing a SignDescriptor with a nil-valued DoubleTweak results in - // deserializing a zero-length slice. Since a nil-valued DoubleTweak has - // special meaning and a zero-length slice for a DoubleTweak is invalid, - // we can use the zero-length slice as the flag for a nil-valued - // DoubleTweak. + // Serializing a SignDescriptor with a nil-valued DoubleTweak results + // in deserializing a zero-length slice. Since a nil-valued DoubleTweak + // has special meaning and a zero-length slice for a DoubleTweak is + // invalid, we can use the zero-length slice as the flag for a + // nil-valued DoubleTweak. if len(doubleTweakBytes) == 0 { sd.DoubleTweak = nil } else { diff --git a/lnwallet/witnessgen.go b/lnwallet/witnessgen.go index c48b7f900..59a7a534d 100644 --- a/lnwallet/witnessgen.go +++ b/lnwallet/witnessgen.go @@ -27,22 +27,42 @@ const ( // commitment transaction. CommitmentRevoke WitnessType = 2 - // HtlcOfferedRevoke is a witness that allows us to sweep an HTLC - // output that we offered to the counterparty. + // HtlcOfferedRevoke is a witness that allows us to sweep an HTLC which + // we offered to the remote party in the case that they broadcast a + // revoked commitmetn state. HtlcOfferedRevoke WitnessType = 3 // HtlcAcceptedRevoke is a witness that allows us to sweep an HTLC - // output that we accepted from the counterparty. + // output sent to us in the case that the remote party broadcasts a + // revoked commitment state. HtlcAcceptedRevoke WitnessType = 4 - // HtlcOfferedTimeout is a witness that allows us to sweep an HTLC - // output that we extended to a party, but was never fulfilled. - HtlcOfferedTimeout WitnessType = 5 + // HtlcOfferedTimeoutSecondLevel is a witness that allows us to sweep + // an HTLC output that we extended to a party, but was never fulfilled. + // This HTLC output isn't directly on the commitment transaction, but + // is the result of a confirmed second-level HTLC transaction. As a + // result, we can only spend this after a CSV delay. + HtlcOfferedTimeoutSecondLevel WitnessType = 5 - // HtlcAcceptedSuccess is a witness that allows us to sweep an HTLC - // output that was offered to us, and for which we have a payment - // preimage. - HtlcAcceptedSuccess WitnessType = 6 + // HtlcAcceptedSuccessSecondLevel is a witness that allows us to sweep + // an HTLC output that was offered to us, and for which we have a + // payment preimage. This HTLC output isn't diretly on our commitment + // transaction, but is the result of confirmed second-level HTLC + // transaction. As a result, we can only spend this after a CSV delay. + HtlcAcceptedSuccessSecondLevel WitnessType = 6 + + // HtlcOfferedRemoteTimeout is a witness that allows us to sweep an + // HTLC that we offered to the remote party which lies in the + // commitment transaction of the remote party. We can spend this output + // after the absolute CLTV timeout of the HTLC as passed. + HtlcOfferedRemoteTimeout WitnessType = 7 + + // HtlcAcceptedRemoteSuccess is a witness that allows us to sweep an + // HTLC that was offered to us by the remote party. We use this witness + // in the case that the remote party goes to chain, and we know the + // pre-image to the HTLC. We can sweep this without any additional + // timeout. + HtlcAcceptedRemoteSuccess WitnessType = 8 ) // WitnessGenerator represents a function which is able to generate the final @@ -66,16 +86,31 @@ func (wt WitnessType) GenWitnessFunc(signer Signer, switch wt { case CommitmentTimeLock: return CommitSpendTimeout(signer, desc, tx) + case CommitmentNoDelay: return CommitSpendNoDelay(signer, desc, tx) + case CommitmentRevoke: return CommitSpendRevoke(signer, desc, tx) + case HtlcOfferedRevoke: return ReceiverHtlcSpendRevoke(signer, desc, tx) + case HtlcAcceptedRevoke: return SenderHtlcSpendRevoke(signer, desc, tx) - case HtlcOfferedTimeout: - return HtlcSpendSuccess(signer, desc, tx) + + case HtlcOfferedTimeoutSecondLevel: + return HtlcSecondLevelSpend(signer, desc, tx) + + case HtlcAcceptedSuccessSecondLevel: + return HtlcSecondLevelSpend(signer, desc, tx) + + case HtlcOfferedRemoteTimeout: + // We pass in a value of -1 for the timeout, as we + // expect the caller to have already set the lock time + // value. + return receiverHtlcSpendTimeout(signer, desc, tx, -1) + default: return nil, fmt.Errorf("unknown witness type: %v", wt) }