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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 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