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

lightningnetwork / lnd / 16876544764

11 Aug 2025 09:47AM UTC coverage: 66.777% (-0.1%) from 66.916%
16876544764

Pull #10136

github

web-flow
Merge 5c940b48d into 72e9ad8e6
Pull Request #10136: lnwallet: use btcwallet's new interface

20 of 21 new or added lines in 2 files covered. (95.24%)

346 existing lines in 28 files now uncovered.

135428 of 202805 relevant lines covered (66.78%)

21645.62 hits per line

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

83.45
/keychain/btcwallet.go
1
package keychain
2

3
import (
4
        "crypto/sha256"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
9
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
10
        "github.com/btcsuite/btcd/chaincfg/chainhash"
11
        "github.com/btcsuite/btcd/txscript"
12
        "github.com/btcsuite/btcwallet/waddrmgr"
13
        "github.com/btcsuite/btcwallet/wallet"
14
        "github.com/btcsuite/btcwallet/walletdb"
15
)
16

17
const (
18
        // CoinTypeBitcoin specifies the BIP44 coin type for Bitcoin key
19
        // derivation.
20
        CoinTypeBitcoin uint32 = 0
21

22
        // CoinTypeTestnet specifies the BIP44 coin type for all testnet key
23
        // derivation.
24
        CoinTypeTestnet = 1
25
)
26

27
var (
28
        // lightningAddrSchema is the scope addr schema for all keys that we
29
        // derive. We'll treat them all as p2wkh addresses, as atm we must
30
        // specify a particular type.
31
        lightningAddrSchema = waddrmgr.ScopeAddrSchema{
32
                ExternalAddrType: waddrmgr.WitnessPubKey,
33
                InternalAddrType: waddrmgr.WitnessPubKey,
34
        }
35

36
        // waddrmgrNamespaceKey is the namespace key that the waddrmgr state is
37
        // stored within the top-level waleltdb buckets of btcwallet.
38
        waddrmgrNamespaceKey = []byte("waddrmgr")
39
)
40

41
// BtcWalletKeyRing is an implementation of both the KeyRing and SecretKeyRing
42
// interfaces backed by btcwallet's internal root waddrmgr. Internally, we'll
43
// be using a ScopedKeyManager to do all of our derivations, using the key
44
// scope and scope addr scehma defined above. Re-using the existing key scope
45
// construction means that all key derivation will be protected under the root
46
// seed of the wallet, making each derived key fully deterministic.
47
type BtcWalletKeyRing struct {
48
        // wallet is a pointer to the active instance of the btcwallet core.
49
        // This is required as we'll need to manually open database
50
        // transactions in order to derive addresses and lookup relevant keys
51
        wallet wallet.Interface
52

53
        // chainKeyScope defines the purpose and coin type to be used when generating
54
        // keys for this keyring.
55
        chainKeyScope waddrmgr.KeyScope
56

57
        // lightningScope is a pointer to the scope that we'll be using as a
58
        // sub key manager to derive all the keys that we require.
59
        lightningScope *waddrmgr.ScopedKeyManager
60
}
61

62
// NewBtcWalletKeyRing creates a new implementation of the
63
// keychain.SecretKeyRing interface backed by btcwallet.
64
//
65
// NOTE: The passed waddrmgr.Manager MUST be unlocked in order for the keychain
66
// to function.
67
func NewBtcWalletKeyRing(w wallet.Interface, coinType uint32) SecretKeyRing {
14✔
68
        // Construct the key scope that will be used within the waddrmgr to
14✔
69
        // create an HD chain for deriving all of our required keys. A different
14✔
70
        // scope is used for each specific coin type.
14✔
71
        chainKeyScope := waddrmgr.KeyScope{
14✔
72
                Purpose: BIP0043Purpose,
14✔
73
                Coin:    coinType,
14✔
74
        }
14✔
75

14✔
76
        return &BtcWalletKeyRing{
14✔
77
                wallet:        w,
14✔
78
                chainKeyScope: chainKeyScope,
14✔
79
        }
14✔
80
}
14✔
81

82
// keyScope attempts to return the key scope that we'll use to derive all of
83
// our keys. If the scope has already been fetched from the database, then a
84
// cached version will be returned. Otherwise, we'll fetch it from the database
85
// and cache it for subsequent accesses.
86
func (b *BtcWalletKeyRing) keyScope() (*waddrmgr.ScopedKeyManager, error) {
717✔
87
        // If the scope has already been populated, then we'll return it
717✔
88
        // directly.
717✔
89
        if b.lightningScope != nil {
1,422✔
90
                return b.lightningScope, nil
705✔
91
        }
705✔
92

93
        // Otherwise, we'll first do a check to ensure that the root manager
94
        // isn't locked, as otherwise we won't be able to *use* the scope.
95
        if !b.wallet.AddrManager().WatchOnly() &&
14✔
96
                b.wallet.AddrManager().IsLocked() {
14✔
NEW
97

×
98
                return nil, fmt.Errorf("cannot create BtcWalletKeyRing with " +
×
99
                        "locked waddrmgr.Manager")
×
100
        }
×
101

102
        // If the manager is indeed unlocked, then we'll fetch the scope, cache
103
        // it, and return to the caller.
104
        lnScope, err := b.wallet.AddrManager().FetchScopedKeyManager(
14✔
105
                b.chainKeyScope,
14✔
106
        )
14✔
107
        if err != nil {
14✔
108
                return nil, err
×
109
        }
×
110

111
        b.lightningScope = lnScope
14✔
112

14✔
113
        return lnScope, nil
14✔
114
}
115

116
// createAccountIfNotExists will create the corresponding account for a key
117
// family if it doesn't already exist in the database.
118
func (b *BtcWalletKeyRing) createAccountIfNotExists(
119
        addrmgrNs walletdb.ReadWriteBucket, keyFam KeyFamily,
120
        scope *waddrmgr.ScopedKeyManager) error {
697✔
121

697✔
122
        // If this is the multi-sig key family, then we can return early as
697✔
123
        // this is the default account that's created.
697✔
124
        if keyFam == KeyFamilyMultiSig {
798✔
125
                return nil
101✔
126
        }
101✔
127

128
        // Otherwise, we'll check if the account already exists, if so, we can
129
        // once again bail early.
130
        _, err := scope.AccountName(addrmgrNs, uint32(keyFam))
598✔
131
        if err == nil {
1,160✔
132
                return nil
562✔
133
        }
562✔
134

135
        // If we reach this point, then the account hasn't yet been created, so
136
        // we'll need to create it before we can proceed.
137
        return scope.NewRawAccount(addrmgrNs, uint32(keyFam))
38✔
138
}
139

140
// DeriveNextKey attempts to derive the *next* key within the key family
141
// (account in BIP43) specified. This method should return the next external
142
// child within this branch.
143
//
144
// NOTE: This is part of the keychain.KeyRing interface.
145
func (b *BtcWalletKeyRing) DeriveNextKey(keyFam KeyFamily) (KeyDescriptor, error) {
333✔
146
        var (
333✔
147
                pubKey *btcec.PublicKey
333✔
148
                keyLoc KeyLocator
333✔
149
        )
333✔
150

333✔
151
        db := b.wallet.Database()
333✔
152
        err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
666✔
153
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
333✔
154

333✔
155
                scope, err := b.keyScope()
333✔
156
                if err != nil {
333✔
157
                        return err
×
158
                }
×
159

160
                // If the account doesn't exist, then we may need to create it
161
                // for the first time in order to derive the keys that we
162
                // require.
163
                err = b.createAccountIfNotExists(addrmgrNs, keyFam, scope)
333✔
164
                if err != nil {
333✔
165
                        return err
×
166
                }
×
167

168
                addrs, err := scope.NextExternalAddresses(
333✔
169
                        addrmgrNs, uint32(keyFam), 1,
333✔
170
                )
333✔
171
                if err != nil {
333✔
172
                        return err
×
173
                }
×
174

175
                // Extract the first address, ensuring that it is of the proper
176
                // interface type, otherwise we can't manipulate it below.
177
                addr, ok := addrs[0].(waddrmgr.ManagedPubKeyAddress)
333✔
178
                if !ok {
333✔
179
                        return fmt.Errorf("address is not a managed pubkey " +
×
180
                                "addr")
×
181
                }
×
182

183
                pubKey = addr.PubKey()
333✔
184

333✔
185
                _, pathInfo, _ := addr.DerivationInfo()
333✔
186
                keyLoc = KeyLocator{
333✔
187
                        Family: keyFam,
333✔
188
                        Index:  pathInfo.Index,
333✔
189
                }
333✔
190

333✔
191
                return nil
333✔
192
        })
193
        if err != nil {
333✔
194
                return KeyDescriptor{}, err
×
195
        }
×
196

197
        return KeyDescriptor{
333✔
198
                PubKey:     pubKey,
333✔
199
                KeyLocator: keyLoc,
333✔
200
        }, nil
333✔
201
}
202

203
// DeriveKey attempts to derive an arbitrary key specified by the passed
204
// KeyLocator. This may be used in several recovery scenarios, or when manually
205
// rotating something like our current default node key.
206
//
207
// NOTE: This is part of the keychain.KeyRing interface.
208
func (b *BtcWalletKeyRing) DeriveKey(keyLoc KeyLocator) (KeyDescriptor, error) {
282✔
209
        var keyDesc KeyDescriptor
282✔
210

282✔
211
        db := b.wallet.Database()
282✔
212
        err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
564✔
213
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
282✔
214

282✔
215
                scope, err := b.keyScope()
282✔
216
                if err != nil {
282✔
217
                        return err
×
218
                }
×
219

220
                // If the account doesn't exist, then we may need to create it
221
                // for the first time in order to derive the keys that we
222
                // require. We skip this if we're using a remote signer in which
223
                // case we _need_ to create all accounts when creating the
224
                // wallet, so it must exist now.
225
                if !b.wallet.AddrManager().WatchOnly() {
564✔
226
                        err = b.createAccountIfNotExists(
282✔
227
                                addrmgrNs, keyLoc.Family, scope,
282✔
228
                        )
282✔
229
                        if err != nil {
282✔
230
                                return err
×
231
                        }
×
232
                }
233

234
                path := waddrmgr.DerivationPath{
282✔
235
                        InternalAccount: uint32(keyLoc.Family),
282✔
236
                        Branch:          0,
282✔
237
                        Index:           keyLoc.Index,
282✔
238
                }
282✔
239
                addr, err := scope.DeriveFromKeyPath(addrmgrNs, path)
282✔
240
                if err != nil {
282✔
241
                        return err
×
242
                }
×
243

244
                keyDesc.KeyLocator = keyLoc
282✔
245
                keyDesc.PubKey = addr.(waddrmgr.ManagedPubKeyAddress).PubKey()
282✔
246

282✔
247
                return nil
282✔
248
        })
249
        if err != nil {
282✔
250
                return keyDesc, err
×
251
        }
×
252

253
        return keyDesc, nil
282✔
254
}
255

256
// DerivePrivKey attempts to derive the private key that corresponds to the
257
// passed key descriptor.
258
//
259
// NOTE: This is part of the keychain.SecretKeyRing interface.
260
func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (
261
        *btcec.PrivateKey, error) {
106✔
262

106✔
263
        var key *btcec.PrivateKey
106✔
264

106✔
265
        scope, err := b.keyScope()
106✔
266
        if err != nil {
106✔
267
                return nil, err
×
268
        }
×
269

270
        // First, attempt to see if we can read the key directly from
271
        // btcwallet's internal cache, if we can then we can skip all the
272
        // operations below (fast path).
273
        if keyDesc.PubKey == nil {
128✔
274
                keyPath := waddrmgr.DerivationPath{
22✔
275
                        InternalAccount: uint32(keyDesc.Family),
22✔
276
                        Account:         uint32(keyDesc.Family),
22✔
277
                        Branch:          0,
22✔
278
                        Index:           keyDesc.Index,
22✔
279
                }
22✔
280
                privKey, err := scope.DeriveFromKeyPathCache(keyPath)
22✔
281
                if err == nil {
44✔
282
                        return privKey, nil
22✔
283
                }
22✔
284
        }
285

286
        db := b.wallet.Database()
86✔
287
        err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
172✔
288
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
86✔
289

86✔
290
                // If the account doesn't exist, then we may need to create it
86✔
291
                // for the first time in order to derive the keys that we
86✔
292
                // require. We skip this if we're using a remote signer in which
86✔
293
                // case we _need_ to create all accounts when creating the
86✔
294
                // wallet, so it must exist now.
86✔
295
                if !b.wallet.AddrManager().WatchOnly() {
172✔
296
                        err = b.createAccountIfNotExists(
86✔
297
                                addrmgrNs, keyDesc.Family, scope,
86✔
298
                        )
86✔
299
                        if err != nil {
86✔
300
                                return err
×
301
                        }
×
302
                }
303

304
                // If the public key isn't set or they have a non-zero index,
305
                // then we know that the caller instead knows the derivation
306
                // path for a key.
307
                if keyDesc.PubKey == nil || keyDesc.Index > 0 {
132✔
308
                        // Now that we know the account exists, we can safely
46✔
309
                        // derive the full private key from the given path.
46✔
310
                        path := waddrmgr.DerivationPath{
46✔
311
                                InternalAccount: uint32(keyDesc.Family),
46✔
312
                                Branch:          0,
46✔
313
                                Index:           keyDesc.Index,
46✔
314
                        }
46✔
315
                        addr, err := scope.DeriveFromKeyPath(addrmgrNs, path)
46✔
316
                        if err != nil {
46✔
317
                                return err
×
318
                        }
×
319

320
                        key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
46✔
321
                        if err != nil {
46✔
322
                                return err
×
323
                        }
×
324

325
                        return nil
46✔
326
                }
327

328
                // If the public key isn't nil, then this indicates that we
329
                // need to scan for the private key, assuming that we know the
330
                // valid key family.
331
                nextPath := waddrmgr.DerivationPath{
42✔
332
                        InternalAccount: uint32(keyDesc.Family),
42✔
333
                        Branch:          0,
42✔
334
                        Index:           0,
42✔
335
                }
42✔
336

42✔
337
                // We'll now iterate through our key range in an attempt to
42✔
338
                // find the target public key.
42✔
339
                //
42✔
340
                // TODO(roasbeef): possibly move scanning into wallet to allow
42✔
341
                // to be parallelized
42✔
342
                for i := 0; i < MaxKeyRangeScan; i++ {
124✔
343
                        // Derive the next key in the range and fetch its
82✔
344
                        // managed address.
82✔
345
                        addr, err := scope.DeriveFromKeyPath(
82✔
346
                                addrmgrNs, nextPath,
82✔
347
                        )
82✔
348
                        if err != nil {
82✔
349
                                return err
×
350
                        }
×
351
                        managedAddr := addr.(waddrmgr.ManagedPubKeyAddress)
82✔
352

82✔
353
                        // If this is the target public key, then we'll return
82✔
354
                        // it directly back to the caller.
82✔
355
                        if managedAddr.PubKey().IsEqual(keyDesc.PubKey) {
104✔
356
                                key, err = managedAddr.PrivKey()
22✔
357
                                if err != nil {
22✔
358
                                        return err
×
359
                                }
×
360

361
                                return nil
22✔
362
                        }
363

364
                        // This wasn't the target key, so roll forward and try
365
                        // the next one.
366
                        nextPath.Index++
62✔
367
                }
368

369
                // If we reach this point, then we we're unable to derive the
370
                // private key, so return an error back to the user.
371
                return ErrCannotDerivePrivKey
20✔
372
        })
373
        if err != nil {
106✔
374
                return nil, err
20✔
375
        }
20✔
376

377
        return key, nil
66✔
378
}
379

380
// ECDH performs a scalar multiplication (ECDH-like operation) between the
381
// target key descriptor and remote public key. The output returned will be
382
// the sha256 of the resulting shared point serialized in compressed format. If
383
// k is our private key, and P is the public key, we perform the following
384
// operation:
385
//
386
//        sx := k*P s := sha256(sx.SerializeCompressed())
387
//
388
// NOTE: This is part of the keychain.ECDHRing interface.
389
func (b *BtcWalletKeyRing) ECDH(keyDesc KeyDescriptor,
390
        pub *btcec.PublicKey) ([32]byte, error) {
46✔
391

46✔
392
        privKey, err := b.DerivePrivKey(keyDesc)
46✔
393
        if err != nil {
46✔
394
                return [32]byte{}, err
×
395
        }
×
396

397
        var (
46✔
398
                pubJacobian btcec.JacobianPoint
46✔
399
                s           btcec.JacobianPoint
46✔
400
        )
46✔
401
        pub.AsJacobian(&pubJacobian)
46✔
402

46✔
403
        btcec.ScalarMultNonConst(&privKey.Key, &pubJacobian, &s)
46✔
404
        s.ToAffine()
46✔
405
        sPubKey := btcec.NewPublicKey(&s.X, &s.Y)
46✔
406
        h := sha256.Sum256(sPubKey.SerializeCompressed())
46✔
407

46✔
408
        return h, nil
46✔
409
}
410

411
// SignMessage signs the given message, single or double SHA256 hashing it
412
// first, with the private key described in the key locator.
413
//
414
// NOTE: This is part of the keychain.MessageSignerRing interface.
415
func (b *BtcWalletKeyRing) SignMessage(keyLoc KeyLocator,
416
        msg []byte, doubleHash bool) (*ecdsa.Signature, error) {
2✔
417

2✔
418
        privKey, err := b.DerivePrivKey(KeyDescriptor{
2✔
419
                KeyLocator: keyLoc,
2✔
420
        })
2✔
421
        if err != nil {
2✔
422
                return nil, err
×
423
        }
×
424

425
        var digest []byte
2✔
426
        if doubleHash {
4✔
427
                digest = chainhash.DoubleHashB(msg)
2✔
428
        } else {
4✔
429
                digest = chainhash.HashB(msg)
2✔
430
        }
2✔
431
        return ecdsa.Sign(privKey, digest), nil
2✔
432
}
433

434
// SignMessageCompact signs the given message, single or double SHA256 hashing
435
// it first, with the private key described in the key locator and returns
436
// the signature in the compact, public key recoverable format.
437
//
438
// NOTE: This is part of the keychain.MessageSignerRing interface.
439
func (b *BtcWalletKeyRing) SignMessageCompact(keyLoc KeyLocator,
440
        msg []byte, doubleHash bool) ([]byte, error) {
2✔
441

2✔
442
        privKey, err := b.DerivePrivKey(KeyDescriptor{
2✔
443
                KeyLocator: keyLoc,
2✔
444
        })
2✔
445
        if err != nil {
2✔
446
                return nil, err
×
447
        }
×
448

449
        var digest []byte
2✔
450
        if doubleHash {
4✔
451
                digest = chainhash.DoubleHashB(msg)
2✔
452
        } else {
4✔
453
                digest = chainhash.HashB(msg)
2✔
454
        }
2✔
455

456
        return ecdsa.SignCompact(privKey, digest, true), nil
2✔
457
}
458

459
// SignMessageSchnorr uses the Schnorr signature algorithm to sign the given
460
// message, single or double SHA256 hashing it first, with the private key
461
// described in the key locator and the optional tweak applied to the private
462
// key.
463
//
464
// NOTE: This is part of the keychain.MessageSignerRing interface.
465
func (b *BtcWalletKeyRing) SignMessageSchnorr(keyLoc KeyLocator,
466
        msg []byte, doubleHash bool, taprootTweak []byte,
467
        tag []byte) (*schnorr.Signature, error) {
2✔
468

2✔
469
        privKey, err := b.DerivePrivKey(KeyDescriptor{
2✔
470
                KeyLocator: keyLoc,
2✔
471
        })
2✔
472
        if err != nil {
2✔
473
                return nil, err
×
474
        }
×
475

476
        if len(taprootTweak) > 0 {
4✔
477
                privKey = txscript.TweakTaprootPrivKey(*privKey, taprootTweak)
2✔
478
        }
2✔
479

480
        // If a tag was provided, we need to take the tagged hash of the input.
481
        var digest []byte
2✔
482
        switch {
2✔
483
        case len(tag) > 0:
2✔
484
                taggedHash := chainhash.TaggedHash(tag, msg)
2✔
485
                digest = taggedHash[:]
2✔
486
        case doubleHash:
×
487
                digest = chainhash.DoubleHashB(msg)
×
488
        default:
2✔
489
                digest = chainhash.HashB(msg)
2✔
490
        }
491
        return schnorr.Sign(privKey, digest)
2✔
492
}
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