From 5bbe126c3431e8b37723f15b41b2aaffd99b72aa Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 18 Jan 2018 13:49:35 -0800 Subject: [PATCH] lnwallet: add new NewUnilateralCloseSummary function In this commit, we add a new function that allows a caller to create a UnilateralCloseSummary with the proper materials. This will be used within a new sub-system to be added in a later commit to properly dispatch notifications when on-chain events happen for a channel. --- lnwallet/channel.go | 94 ++++++++++++++++++++++++++++++++++++++++ lnwallet/channel_test.go | 22 +++------- 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 90322e6a..132d61fe 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -4048,6 +4048,100 @@ type UnilateralCloseSummary struct { // and also any incoming HTLC's that we know the pre-image to. HtlcResolutions *HtlcResolutions + // RemoteCommit is the exact commitment state that the remote party + // broadcast. + RemoteCommit channeldb.ChannelCommitment +} + +// NewUnilateralCloseSummary creates a new summary that provides the caller +// with all the information required to claim all funds on chain in the event +// that the remote party broadcasts their commitment. +func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer, + pCache PreimageCache, commitSpend *chainntnfs.SpendDetail, + remoteCommit channeldb.ChannelCommitment) (*UnilateralCloseSummary, error) { + + // First, we'll generate the commitment point and the revocation point + // so we can re-construct the HTLC state and also our payment key. + commitPoint := chanState.RemoteCurrentRevocation + keyRing := deriveCommitmentKeys( + commitPoint, false, &chanState.LocalChanCfg, + &chanState.RemoteChanCfg, + ) + + // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we + // had on their commitment transaction. + htlcResolutions, err := extractHtlcResolutions( + remoteCommit.FeePerKw, false, signer, remoteCommit.Htlcs, + keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + *commitSpend.SpenderTxHash, pCache, + ) + if err != nil { + return nil, fmt.Errorf("unable to create htlc resolutions: %v", err) + } + + commitTxBroadcast := commitSpend.SpendingTx + + // Before we can generate the proper sign descriptor, we'll need to + // locate the output index of our non-delayed output on the commitment + // transaction. + selfP2WKH, err := commitScriptUnencumbered(keyRing.NoDelayKey) + if err != nil { + return nil, fmt.Errorf("unable to create self commit script: %v", err) + } + var selfPoint *wire.OutPoint + for outputIndex, txOut := range commitTxBroadcast.TxOut { + if bytes.Equal(txOut.PkScript, selfP2WKH) { + selfPoint = &wire.OutPoint{ + Hash: *commitSpend.SpenderTxHash, + Index: uint32(outputIndex), + } + break + } + } + + // With the HTLC's taken care of, we'll generate the sign descriptor + // necessary to sweep our commitment output, but only if we had a + // non-trimmed balance. + var commitResolution *CommitOutputResolution + if selfPoint != nil { + localPayBase := chanState.LocalChanCfg.PaymentBasePoint + localBalance := remoteCommit.LocalBalance.ToSatoshis() + commitResolution = &CommitOutputResolution{ + SelfOutPoint: *selfPoint, + SelfOutputSignDesc: SignDescriptor{ + PubKey: localPayBase, + SingleTweak: keyRing.LocalCommitKeyTweak, + WitnessScript: selfP2WKH, + Output: &wire.TxOut{ + Value: int64(localBalance), + PkScript: selfP2WKH, + }, + HashType: txscript.SigHashAll, + }, + MaturityDelay: 0, + } + } + + localBalance := remoteCommit.LocalBalance.ToSatoshis() + closeSummary := channeldb.ChannelCloseSummary{ + ChanPoint: chanState.FundingOutpoint, + ChainHash: chanState.ChainHash, + ClosingTXID: *commitSpend.SpenderTxHash, + CloseHeight: uint32(commitSpend.SpendingHeight), + RemotePub: chanState.IdentityPub, + Capacity: chanState.Capacity, + SettledBalance: localBalance, + CloseType: channeldb.ForceClose, + IsPending: true, + } + + return &UnilateralCloseSummary{ + SpendDetail: commitSpend, + ChannelCloseSummary: closeSummary, + CommitResolution: commitResolution, + HtlcResolutions: htlcResolutions, + RemoteCommit: remoteCommit, + }, nil } // IncomingHtlcResolution houses the information required to sweep any incoming diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 430576a1..8c1235a8 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4184,26 +4184,18 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // We'll then use Bob's transaction to trigger a spend notification for // Alice. - aliceNotifier := aliceChannel.channelEvents.(*mockNotfier) closeTx := bobForceClose.CloseTx commitTxHash := closeTx.TxHash() - select { - case aliceNotifier.activeSpendNtfn <- &chainntnfs.SpendDetail{ + spendDetail := &chainntnfs.SpendDetail{ SpendingTx: closeTx, SpenderTxHash: &commitTxHash, - }: - case <-time.After(time.Second * 15): - t.Fatalf("alice didn't consume spend ntfn") } - - // Alice should now send over a signal on the unilateral close signal - // that we'll use to ensure she's able to sweep all the relevant - // outputs. - var aliceCloseSummary *UnilateralCloseSummary - select { - case aliceCloseSummary = <-aliceChannel.UnilateralClose: - case <-time.After(time.Second * 15): - t.Fatalf("alice didn't send her close summary") + aliceCloseSummary, err := NewUnilateralCloseSummary( + aliceChannel.channelState, aliceChannel.signer, aliceChannel.pCache, + spendDetail, aliceChannel.channelState.RemoteCommitment, + ) + if err != nil { + t.Fatalf("unable to create alice close summary: %v", err) } // She should detect that she can sweep both the outgoing HTLC as well