• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/channeldb/migration30/lnwallet.go
1
package migration30
2

3
import (
4
        "bytes"
5

6
        "github.com/btcsuite/btcd/btcec/v2"
7
        "github.com/btcsuite/btcd/chaincfg/chainhash"
8
        mig25 "github.com/lightningnetwork/lnd/channeldb/migration25"
9
        mig26 "github.com/lightningnetwork/lnd/channeldb/migration26"
10
        mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
11
        "github.com/lightningnetwork/lnd/input"
12
)
13

14
// CommitmentKeyRing holds all derived keys needed to construct commitment and
15
// HTLC transactions. The keys are derived differently depending whether the
16
// commitment transaction is ours or the remote peer's. Private keys associated
17
// with each key may belong to the commitment owner or the "other party" which
18
// is referred to in the field comments, regardless of which is local and which
19
// is remote.
20
type CommitmentKeyRing struct {
21
        // CommitPoint is the "per commitment point" used to derive the tweak
22
        // for each base point.
23
        CommitPoint *btcec.PublicKey
24

25
        // LocalCommitKeyTweak is the tweak used to derive the local public key
26
        // from the local payment base point or the local private key from the
27
        // base point secret. This may be included in a SignDescriptor to
28
        // generate signatures for the local payment key.
29
        //
30
        // NOTE: This will always refer to "our" local key, regardless of
31
        // whether this is our commit or not.
32
        LocalCommitKeyTweak []byte
33

34
        // TODO(roasbeef): need delay tweak as well?
35

36
        // LocalHtlcKeyTweak is the tweak used to derive the local HTLC key
37
        // from the local HTLC base point. This value is needed in order to
38
        // derive the final key used within the HTLC scripts in the commitment
39
        // transaction.
40
        //
41
        // NOTE: This will always refer to "our" local HTLC key, regardless of
42
        // whether this is our commit or not.
43
        LocalHtlcKeyTweak []byte
44

45
        // LocalHtlcKey is the key that will be used in any clause paying to
46
        // our node of any HTLC scripts within the commitment transaction for
47
        // this key ring set.
48
        //
49
        // NOTE: This will always refer to "our" local HTLC key, regardless of
50
        // whether this is our commit or not.
51
        LocalHtlcKey *btcec.PublicKey
52

53
        // RemoteHtlcKey is the key that will be used in clauses within the
54
        // HTLC script that send money to the remote party.
55
        //
56
        // NOTE: This will always refer to "their" remote HTLC key, regardless
57
        // of whether this is our commit or not.
58
        RemoteHtlcKey *btcec.PublicKey
59

60
        // ToLocalKey is the commitment transaction owner's key which is
61
        // included in HTLC success and timeout transaction scripts. This is
62
        // the public key used for the to_local output of the commitment
63
        // transaction.
64
        //
65
        // NOTE: Who's key this is depends on the current perspective. If this
66
        // is our commitment this will be our key.
67
        ToLocalKey *btcec.PublicKey
68

69
        // ToRemoteKey is the non-owner's payment key in the commitment tx.
70
        // This is the key used to generate the to_remote output within the
71
        // commitment transaction.
72
        //
73
        // NOTE: Who's key this is depends on the current perspective. If this
74
        // is our commitment this will be their key.
75
        ToRemoteKey *btcec.PublicKey
76

77
        // RevocationKey is the key that can be used by the other party to
78
        // redeem outputs from a revoked commitment transaction if it were to
79
        // be published.
80
        //
81
        // NOTE: Who can sign for this key depends on the current perspective.
82
        // If this is our commitment, it means the remote node can sign for
83
        // this key in case of a breach.
84
        RevocationKey *btcec.PublicKey
85
}
86

87
// ScriptInfo holds a redeem script and hash.
88
type ScriptInfo struct {
89
        // PkScript is the output's PkScript.
90
        PkScript []byte
91

92
        // WitnessScript is the full script required to properly redeem the
93
        // output. This field should be set to the full script if a p2wsh
94
        // output is being signed. For p2wkh it should be set equal to the
95
        // PkScript.
96
        WitnessScript []byte
97
}
98

99
// findOutputIndexesFromRemote finds the index of our and their outputs from
100
// the remote commitment transaction. It derives the key ring to compute the
101
// output scripts and compares them against the outputs inside the commitment
102
// to find the match.
103
func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash,
104
        chanState *mig26.OpenChannel,
UNCOV
105
        oldLog *mig.ChannelCommitment) (uint32, uint32, error) {
×
UNCOV
106

×
UNCOV
107
        // Init the output indexes as empty.
×
UNCOV
108
        ourIndex := uint32(OutputIndexEmpty)
×
UNCOV
109
        theirIndex := uint32(OutputIndexEmpty)
×
UNCOV
110

×
UNCOV
111
        chanCommit := oldLog
×
UNCOV
112
        _, commitmentPoint := btcec.PrivKeyFromBytes(revocationPreimage[:])
×
UNCOV
113

×
UNCOV
114
        // With the commitment point generated, we can now derive the king ring
×
UNCOV
115
        // which will be used to generate the output scripts.
×
UNCOV
116
        keyRing := DeriveCommitmentKeys(
×
UNCOV
117
                commitmentPoint, false, chanState.ChanType,
×
UNCOV
118
                &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
×
UNCOV
119
        )
×
UNCOV
120

×
UNCOV
121
        // Since it's remote commitment chain, we'd used the mirrored values.
×
UNCOV
122
        //
×
UNCOV
123
        // We use the remote's channel config for the csv delay.
×
UNCOV
124
        theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
×
UNCOV
125

×
UNCOV
126
        // If we are the initiator of this channel, then it's be false from the
×
UNCOV
127
        // remote's PoV.
×
UNCOV
128
        isRemoteInitiator := !chanState.IsInitiator
×
UNCOV
129

×
UNCOV
130
        var leaseExpiry uint32
×
UNCOV
131
        if chanState.ChanType.HasLeaseExpiration() {
×
132
                leaseExpiry = chanState.ThawHeight
×
133
        }
×
134

135
        // Map the scripts from our PoV. When facing a local commitment, the to
136
        // local output belongs to us and the to remote output belongs to them.
137
        // When facing a remote commitment, the to local output belongs to them
138
        // and the to remote output belongs to us.
139

140
        // Compute the to local script. From our PoV, when facing a remote
141
        // commitment, the to local output belongs to them.
UNCOV
142
        theirScript, err := CommitScriptToSelf(
×
UNCOV
143
                chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
×
UNCOV
144
                keyRing.RevocationKey, theirDelay, leaseExpiry,
×
UNCOV
145
        )
×
UNCOV
146
        if err != nil {
×
147
                return ourIndex, theirIndex, err
×
148
        }
×
149

150
        // Compute the to remote script. From our PoV, when facing a remote
151
        // commitment, the to remote output belongs to us.
UNCOV
152
        ourScript, _, err := CommitScriptToRemote(
×
UNCOV
153
                chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
×
UNCOV
154
                leaseExpiry,
×
UNCOV
155
        )
×
UNCOV
156
        if err != nil {
×
157
                return ourIndex, theirIndex, err
×
158
        }
×
159

160
        // Now compare the scripts to find our/their output index.
UNCOV
161
        for i, txOut := range chanCommit.CommitTx.TxOut {
×
UNCOV
162
                switch {
×
UNCOV
163
                case bytes.Equal(txOut.PkScript, ourScript.PkScript):
×
UNCOV
164
                        ourIndex = uint32(i)
×
165
                case bytes.Equal(txOut.PkScript, theirScript.PkScript):
×
166
                        theirIndex = uint32(i)
×
167
                }
168
        }
169

UNCOV
170
        return ourIndex, theirIndex, nil
×
171
}
172

173
// DeriveCommitmentKeys generates a new commitment key set using the base points
174
// and commitment point. The keys are derived differently depending on the type
175
// of channel, and whether the commitment transaction is ours or the remote
176
// peer's.
177
func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
178
        isOurCommit bool, chanType mig25.ChannelType,
UNCOV
179
        localChanCfg, remoteChanCfg *mig.ChannelConfig) *CommitmentKeyRing {
×
UNCOV
180

×
UNCOV
181
        tweaklessCommit := chanType.IsTweakless()
×
UNCOV
182

×
UNCOV
183
        // Depending on if this is our commit or not, we'll choose the correct
×
UNCOV
184
        // base point.
×
UNCOV
185
        localBasePoint := localChanCfg.PaymentBasePoint
×
UNCOV
186
        if isOurCommit {
×
187
                localBasePoint = localChanCfg.DelayBasePoint
×
188
        }
×
189

190
        // First, we'll derive all the keys that don't depend on the context of
191
        // whose commitment transaction this is.
UNCOV
192
        keyRing := &CommitmentKeyRing{
×
UNCOV
193
                CommitPoint: commitPoint,
×
UNCOV
194

×
UNCOV
195
                LocalCommitKeyTweak: input.SingleTweakBytes(
×
UNCOV
196
                        commitPoint, localBasePoint.PubKey,
×
UNCOV
197
                ),
×
UNCOV
198
                LocalHtlcKeyTweak: input.SingleTweakBytes(
×
UNCOV
199
                        commitPoint, localChanCfg.HtlcBasePoint.PubKey,
×
UNCOV
200
                ),
×
UNCOV
201
                LocalHtlcKey: input.TweakPubKey(
×
UNCOV
202
                        localChanCfg.HtlcBasePoint.PubKey, commitPoint,
×
UNCOV
203
                ),
×
UNCOV
204
                RemoteHtlcKey: input.TweakPubKey(
×
UNCOV
205
                        remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
×
UNCOV
206
                ),
×
UNCOV
207
        }
×
UNCOV
208

×
UNCOV
209
        // We'll now compute the to_local, to_remote, and revocation key based
×
UNCOV
210
        // on the current commitment point. All keys are tweaked each state in
×
UNCOV
211
        // order to ensure the keys from each state are unlinkable. To create
×
UNCOV
212
        // the revocation key, we take the opposite party's revocation base
×
UNCOV
213
        // point and combine that with the current commitment point.
×
UNCOV
214
        var (
×
UNCOV
215
                toLocalBasePoint    *btcec.PublicKey
×
UNCOV
216
                toRemoteBasePoint   *btcec.PublicKey
×
UNCOV
217
                revocationBasePoint *btcec.PublicKey
×
UNCOV
218
        )
×
UNCOV
219
        if isOurCommit {
×
220
                toLocalBasePoint = localChanCfg.DelayBasePoint.PubKey
×
221
                toRemoteBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
×
222
                revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
×
UNCOV
223
        } else {
×
UNCOV
224
                toLocalBasePoint = remoteChanCfg.DelayBasePoint.PubKey
×
UNCOV
225
                toRemoteBasePoint = localChanCfg.PaymentBasePoint.PubKey
×
UNCOV
226
                revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
×
UNCOV
227
        }
×
228

229
        // With the base points assigned, we can now derive the actual keys
230
        // using the base point, and the current commitment tweak.
UNCOV
231
        keyRing.ToLocalKey = input.TweakPubKey(toLocalBasePoint, commitPoint)
×
UNCOV
232
        keyRing.RevocationKey = input.DeriveRevocationPubkey(
×
UNCOV
233
                revocationBasePoint, commitPoint,
×
UNCOV
234
        )
×
UNCOV
235

×
UNCOV
236
        // If this commitment should omit the tweak for the remote point, then
×
UNCOV
237
        // we'll use that directly, and ignore the commitPoint tweak.
×
UNCOV
238
        if tweaklessCommit {
×
UNCOV
239
                keyRing.ToRemoteKey = toRemoteBasePoint
×
UNCOV
240

×
UNCOV
241
                // If this is not our commitment, the above ToRemoteKey will be
×
UNCOV
242
                // ours, and we blank out the local commitment tweak to
×
UNCOV
243
                // indicate that the key should not be tweaked when signing.
×
UNCOV
244
                if !isOurCommit {
×
UNCOV
245
                        keyRing.LocalCommitKeyTweak = nil
×
UNCOV
246
                }
×
247
        } else {
×
248
                keyRing.ToRemoteKey = input.TweakPubKey(
×
249
                        toRemoteBasePoint, commitPoint,
×
250
                )
×
251
        }
×
252

UNCOV
253
        return keyRing
×
254
}
255

256
// CommitScriptToRemote derives the appropriate to_remote script based on the
257
// channel's commitment type. The `initiator` argument should correspond to the
258
// owner of the commitment transaction which we are generating the to_remote
259
// script for. The second return value is the CSV delay of the output script,
260
// what must be satisfied in order to spend the output.
261
func CommitScriptToRemote(chanType mig25.ChannelType, initiator bool,
UNCOV
262
        key *btcec.PublicKey, leaseExpiry uint32) (*ScriptInfo, uint32, error) {
×
UNCOV
263

×
UNCOV
264
        switch {
×
265
        // If we are not the initiator of a leased channel, then the remote
266
        // party has an additional CLTV requirement in addition to the 1 block
267
        // CSV requirement.
268
        case chanType.HasLeaseExpiration() && !initiator:
×
269
                script, err := input.LeaseCommitScriptToRemoteConfirmed(
×
270
                        key, leaseExpiry,
×
271
                )
×
272
                if err != nil {
×
273
                        return nil, 0, err
×
274
                }
×
275

276
                p2wsh, err := input.WitnessScriptHash(script)
×
277
                if err != nil {
×
278
                        return nil, 0, err
×
279
                }
×
280

281
                return &ScriptInfo{
×
282
                        PkScript:      p2wsh,
×
283
                        WitnessScript: script,
×
284
                }, 1, nil
×
285

286
        // If this channel type has anchors, we derive the delayed to_remote
287
        // script.
288
        case chanType.HasAnchors():
×
289
                script, err := input.CommitScriptToRemoteConfirmed(key)
×
290
                if err != nil {
×
291
                        return nil, 0, err
×
292
                }
×
293

294
                p2wsh, err := input.WitnessScriptHash(script)
×
295
                if err != nil {
×
296
                        return nil, 0, err
×
297
                }
×
298

299
                return &ScriptInfo{
×
300
                        PkScript:      p2wsh,
×
301
                        WitnessScript: script,
×
302
                }, 1, nil
×
303

UNCOV
304
        default:
×
UNCOV
305
                // Otherwise the to_remote will be a simple p2wkh.
×
UNCOV
306
                p2wkh, err := input.CommitScriptUnencumbered(key)
×
UNCOV
307
                if err != nil {
×
308
                        return nil, 0, err
×
309
                }
×
310

311
                // Since this is a regular P2WKH, the WitnessScipt and PkScript
312
                // should both be set to the script hash.
UNCOV
313
                return &ScriptInfo{
×
UNCOV
314
                        WitnessScript: p2wkh,
×
UNCOV
315
                        PkScript:      p2wkh,
×
UNCOV
316
                }, 0, nil
×
317
        }
318
}
319

320
// CommitScriptToSelf constructs the public key script for the output on the
321
// commitment transaction paying to the "owner" of said commitment transaction.
322
// The `initiator` argument should correspond to the owner of the commitment
323
// transaction which we are generating the to_local script for. If the other
324
// party learns of the preimage to the revocation hash, then they can claim all
325
// the settled funds in the channel, plus the unsettled funds.
326
func CommitScriptToSelf(chanType mig25.ChannelType, initiator bool,
327
        selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32) (
UNCOV
328
        *ScriptInfo, error) {
×
UNCOV
329

×
UNCOV
330
        var (
×
UNCOV
331
                toLocalRedeemScript []byte
×
UNCOV
332
                err                 error
×
UNCOV
333
        )
×
UNCOV
334
        switch {
×
335
        // If we are the initiator of a leased channel, then we have an
336
        // additional CLTV requirement in addition to the usual CSV requirement.
337
        case initiator && chanType.HasLeaseExpiration():
×
338
                toLocalRedeemScript, err = input.LeaseCommitScriptToSelf(
×
339
                        selfKey, revokeKey, csvDelay, leaseExpiry,
×
340
                )
×
341

UNCOV
342
        default:
×
UNCOV
343
                toLocalRedeemScript, err = input.CommitScriptToSelf(
×
UNCOV
344
                        csvDelay, selfKey, revokeKey,
×
UNCOV
345
                )
×
346
        }
UNCOV
347
        if err != nil {
×
348
                return nil, err
×
349
        }
×
350

UNCOV
351
        toLocalScriptHash, err := input.WitnessScriptHash(toLocalRedeemScript)
×
UNCOV
352
        if err != nil {
×
353
                return nil, err
×
354
        }
×
355

UNCOV
356
        return &ScriptInfo{
×
UNCOV
357
                PkScript:      toLocalScriptHash,
×
UNCOV
358
                WitnessScript: toLocalRedeemScript,
×
UNCOV
359
        }, nil
×
360
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc