diff --git a/lnwallet/channel.go b/lnwallet/channel.go index c60c591e..79722da8 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -773,6 +773,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, ourWitnessScript, theirWitnessScript []byte pd PaymentDescriptor err error + chanType = lc.channelState.ChanType ) // If the either outputs is dust from the local or remote node's @@ -784,8 +785,9 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit) if !isDustLocal && localCommitKeys != nil { ourP2WSH, ourWitnessScript, err = genHtlcScript( - htlc.Incoming, true, htlc.RefundTimeout, htlc.RHash, - localCommitKeys) + chanType, htlc.Incoming, true, htlc.RefundTimeout, + htlc.RHash, localCommitKeys, + ) if err != nil { return pd, err } @@ -794,8 +796,9 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit) if !isDustRemote && remoteCommitKeys != nil { theirP2WSH, theirWitnessScript, err = genHtlcScript( - htlc.Incoming, false, htlc.RefundTimeout, htlc.RHash, - remoteCommitKeys) + chanType, htlc.Incoming, false, htlc.RefundTimeout, + htlc.RHash, remoteCommitKeys, + ) if err != nil { return pd, err } @@ -1410,7 +1413,8 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, wireMsg.Amount.ToSatoshis(), remoteDustLimit) if !isDustRemote { theirP2WSH, theirWitnessScript, err := genHtlcScript( - false, false, wireMsg.Expiry, wireMsg.PaymentHash, + lc.channelState.ChanType, false, false, + wireMsg.Expiry, wireMsg.PaymentHash, remoteCommitKeys, ) if err != nil { @@ -2184,8 +2188,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // an outgoing HTLC that we sent, then from the PoV of the // remote commitment state, they're the receiver of this HTLC. htlcPkScript, htlcWitnessScript, err := genHtlcScript( - htlc.Incoming, false, htlc.RefundTimeout, - htlc.RHash, keyRing, + chanState.ChanType, htlc.Incoming, false, + htlc.RefundTimeout, htlc.RHash, keyRing, ) if err != nil { return nil, err @@ -2714,6 +2718,7 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64, // signature can be submitted to the sigPool to generate all the signatures // asynchronously and in parallel. func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, + chanType channeldb.ChannelType, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, remoteCommitView *commitment) ([]SignJob, chan struct{}, error) { @@ -2761,7 +2766,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, Index: uint32(htlc.remoteOutputIndex), } sigJob.Tx, err = createHtlcTimeoutTx( - op, outputAmt, htlc.Timeout, + chanType, op, outputAmt, htlc.Timeout, uint32(remoteChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, ) @@ -2813,7 +2818,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, Index: uint32(htlc.remoteOutputIndex), } sigJob.Tx, err = createHtlcSuccessTx( - op, outputAmt, uint32(remoteChanCfg.CsvDelay), + chanType, op, outputAmt, uint32(remoteChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, ) if err != nil { @@ -3318,7 +3323,8 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch // need to generate signatures of each of them for the remote party's // commitment state. We do so in two phases: first we generate and // submit the set of signature jobs to the worker pool. - sigBatch, cancelChan, err := genRemoteHtlcSigJobs(keyRing, + sigBatch, cancelChan, err := genRemoteHtlcSigJobs( + keyRing, lc.channelState.ChanType, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, newCommitView, ) @@ -3776,6 +3782,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool, // directly into the pool of workers. func genHtlcSigValidationJobs(localCommitmentView *commitment, keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, + chanType channeldb.ChannelType, localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]VerifyJob, error) { txHash := localCommitmentView.txn.TxHash() @@ -3822,9 +3829,11 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, htlcFee := htlcSuccessFee(feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee - successTx, err := createHtlcSuccessTx(op, - outputAmt, uint32(localChanCfg.CsvDelay), - keyRing.RevocationKey, keyRing.ToLocalKey) + successTx, err := createHtlcSuccessTx( + chanType, op, outputAmt, + uint32(localChanCfg.CsvDelay), + keyRing.RevocationKey, keyRing.ToLocalKey, + ) if err != nil { return nil, err } @@ -3874,8 +3883,8 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment, htlcFee := htlcTimeoutFee(feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee - timeoutTx, err := createHtlcTimeoutTx(op, - outputAmt, htlc.Timeout, + timeoutTx, err := createHtlcTimeoutTx( + chanType, op, outputAmt, htlc.Timeout, uint32(localChanCfg.CsvDelay), keyRing.RevocationKey, keyRing.ToLocalKey, ) @@ -4092,7 +4101,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, // generated, we'll submit these jobs to the worker pool. verifyJobs, err := genHtlcSigValidationJobs( localCommitmentView, keyRing, htlcSigs, - &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, + lc.channelState.ChanType, &lc.channelState.LocalChanCfg, + &lc.channelState.RemoteChanCfg, ) if err != nil { return err @@ -5100,6 +5110,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si chainfee.SatPerKWeight(remoteCommit.FeePerKw), false, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, *commitSpend.SpenderTxHash, + chanState.ChanType, ) if err != nil { return nil, fmt.Errorf("unable to create htlc "+ @@ -5295,7 +5306,7 @@ func newOutgoingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay uint32, - localCommit bool) (*OutgoingHtlcResolution, error) { + localCommit bool, chanType channeldb.ChannelType) (*OutgoingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitHash, @@ -5305,7 +5316,8 @@ func newOutgoingHtlcResolution(signer input.Signer, // First, we'll re-generate the script used to send the HTLC to // the remote party within their commitment transaction. htlcScriptHash, htlcScript, err := genHtlcScript( - false, localCommit, htlc.RefundTimeout, htlc.RHash, keyRing, + chanType, false, localCommit, htlc.RefundTimeout, htlc.RHash, + keyRing, ) if err != nil { return nil, err @@ -5345,8 +5357,8 @@ func newOutgoingHtlcResolution(signer input.Signer, // With the fee calculated, re-construct the second level timeout // transaction. timeoutTx, err := createHtlcTimeoutTx( - op, secondLevelOutputAmt, htlc.RefundTimeout, csvDelay, - keyRing.RevocationKey, keyRing.ToLocalKey, + chanType, op, secondLevelOutputAmt, htlc.RefundTimeout, + csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, ) if err != nil { return nil, err @@ -5422,10 +5434,11 @@ func newOutgoingHtlcResolution(signer input.Signer, // they can just sweep the output immediately with knowledge of the pre-image. // // TODO(roasbeef) consolidate code with above func -func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.ChannelConfig, - commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, - feePerKw chainfee.SatPerKWeight, csvDelay uint32, - localCommit bool) (*IncomingHtlcResolution, error) { +func newIncomingHtlcResolution(signer input.Signer, + localChanCfg *channeldb.ChannelConfig, commitHash chainhash.Hash, + htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, + feePerKw chainfee.SatPerKWeight, csvDelay uint32, localCommit bool, + chanType channeldb.ChannelType) (*IncomingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitHash, @@ -5435,7 +5448,8 @@ func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan // First, we'll re-generate the script the remote party used to // send the HTLC to us in their commitment transaction. htlcScriptHash, htlcScript, err := genHtlcScript( - true, localCommit, htlc.RefundTimeout, htlc.RHash, keyRing, + chanType, true, localCommit, htlc.RefundTimeout, htlc.RHash, + keyRing, ) if err != nil { return nil, err @@ -5469,7 +5483,7 @@ func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan htlcFee := htlcSuccessFee(feePerKw) secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee successTx, err := createHtlcSuccessTx( - op, secondLevelOutputAmt, csvDelay, + chanType, op, secondLevelOutputAmt, csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, ) if err != nil { @@ -5569,7 +5583,8 @@ func (r *OutgoingHtlcResolution) HtlcPoint() wire.OutPoint { func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, signer input.Signer, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, - commitHash chainhash.Hash) (*HtlcResolutions, error) { + commitHash chainhash.Hash, chanType channeldb.ChannelType) ( + *HtlcResolutions, error) { // TODO(roasbeef): don't need to swap csv delay? dustLimit := remoteChanCfg.DustLimit @@ -5582,6 +5597,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, incomingResolutions := make([]IncomingHtlcResolution, 0, len(htlcs)) outgoingResolutions := make([]OutgoingHtlcResolution, 0, len(htlcs)) for _, htlc := range htlcs { + htlc := htlc + // We'll skip any HTLC's which were dust on the commitment // transaction, as these don't have a corresponding output // within the commitment transaction. @@ -5596,8 +5613,9 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, // Otherwise, we'll create an incoming HTLC resolution // as we can satisfy the contract. ihr, err := newIncomingHtlcResolution( - signer, localChanCfg, commitHash, &htlc, keyRing, - feePerKw, uint32(csvDelay), ourCommit, + signer, localChanCfg, commitHash, &htlc, + keyRing, feePerKw, uint32(csvDelay), ourCommit, + chanType, ) if err != nil { return nil, err @@ -5609,7 +5627,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool, ohr, err := newOutgoingHtlcResolution( signer, localChanCfg, commitHash, &htlc, keyRing, - feePerKw, uint32(csvDelay), ourCommit, + feePerKw, uint32(csvDelay), ourCommit, chanType, ) if err != nil { return nil, err @@ -5788,7 +5806,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si htlcResolutions, err := extractHtlcResolutions( chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg, txHash, + &chanState.RemoteChanCfg, txHash, chanState.ChanType, ) if err != nil { return nil, err diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 459dd83b..5d56fcd9 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -361,7 +361,10 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, continue } - err := addHTLC(commitTx, isOurs, false, htlc, keyRing) + err := addHTLC( + commitTx, isOurs, false, htlc, keyRing, + cb.chanState.ChanType, + ) if err != nil { return nil, err } @@ -373,7 +376,10 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, continue } - err := addHTLC(commitTx, isOurs, true, htlc, keyRing) + err := addHTLC( + commitTx, isOurs, true, htlc, keyRing, + cb.chanState.ChanType, + ) if err != nil { return nil, err } @@ -485,7 +491,8 @@ func CreateCommitTx(chanType channeldb.ChannelType, // genHtlcScript generates the proper P2WSH public key scripts for the HTLC // output modified by two-bits denoting if this is an incoming HTLC, and if the // HTLC is being applied to their commitment transaction or ours. -func genHtlcScript(isIncoming, ourCommit bool, timeout uint32, rHash [32]byte, +func genHtlcScript(chanType channeldb.ChannelType, isIncoming, ourCommit bool, + timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing) ([]byte, []byte, error) { var ( @@ -549,13 +556,14 @@ func genHtlcScript(isIncoming, ourCommit bool, timeout uint32, rHash [32]byte, // the descriptor itself. func addHTLC(commitTx *wire.MsgTx, ourCommit bool, isIncoming bool, paymentDesc *PaymentDescriptor, - keyRing *CommitmentKeyRing) error { + keyRing *CommitmentKeyRing, chanType channeldb.ChannelType) error { timeout := paymentDesc.Timeout rHash := paymentDesc.RHash - p2wsh, witnessScript, err := genHtlcScript(isIncoming, ourCommit, - timeout, rHash, keyRing) + p2wsh, witnessScript, err := genHtlcScript( + chanType, isIncoming, ourCommit, timeout, rHash, keyRing, + ) if err != nil { return err } diff --git a/lnwallet/transactions.go b/lnwallet/transactions.go index 7803f752..c8f14a96 100644 --- a/lnwallet/transactions.go +++ b/lnwallet/transactions.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" ) @@ -44,8 +45,8 @@ var ( // In order to spend the HTLC output, the witness for the passed transaction // should be: // * <0> -func createHtlcSuccessTx(htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, - csvDelay uint32, +func createHtlcSuccessTx(chanType channeldb.ChannelType, + htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay uint32, revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) { // Create a version two transaction (as the success version of this @@ -97,7 +98,8 @@ func createHtlcSuccessTx(htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, // 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(htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, +func createHtlcTimeoutTx(chanType channeldb.ChannelType, + htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, cltvExpiry, csvDelay uint32, revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) { diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 4f5cc65a..db5a8dce 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -851,9 +851,10 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { // Generate second-level HTLC transactions for HTLCs in // commitment tx. htlcResolutions, err := extractHtlcResolutions( - chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer, - htlcs, keys, &channel.channelState.LocalChanCfg, + chainfee.SatPerKWeight(test.commitment.FeePerKw), true, + signer, htlcs, keys, &channel.channelState.LocalChanCfg, &channel.channelState.RemoteChanCfg, commitTx.TxHash(), + channel.channelState.ChanType, ) if err != nil { t.Errorf("Case %d: Failed to extract HTLC resolutions: %v", i, err)