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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 hits per line

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

68.21
/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.Wallet
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.Wallet, coinType uint32) SecretKeyRing {
12✔
68
        // Construct the key scope that will be used within the waddrmgr to
12✔
69
        // create an HD chain for deriving all of our required keys. A different
12✔
70
        // scope is used for each specific coin type.
12✔
71
        chainKeyScope := waddrmgr.KeyScope{
12✔
72
                Purpose: BIP0043Purpose,
12✔
73
                Coin:    coinType,
12✔
74
        }
12✔
75

12✔
76
        return &BtcWalletKeyRing{
12✔
77
                wallet:        w,
12✔
78
                chainKeyScope: chainKeyScope,
12✔
79
        }
12✔
80
}
12✔
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) {
715✔
87
        // If the scope has already been populated, then we'll return it
715✔
88
        // directly.
715✔
89
        if b.lightningScope != nil {
1,418✔
90
                return b.lightningScope, nil
703✔
91
        }
703✔
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.Manager.WatchOnly() && b.wallet.Manager.IsLocked() {
12✔
96
                return nil, fmt.Errorf("cannot create BtcWalletKeyRing with " +
×
97
                        "locked waddrmgr.Manager")
×
98
        }
×
99

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

107
        b.lightningScope = lnScope
12✔
108

12✔
109
        return lnScope, nil
12✔
110
}
111

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

695✔
118
        // If this is the multi-sig key family, then we can return early as
695✔
119
        // this is the default account that's created.
695✔
120
        if keyFam == KeyFamilyMultiSig {
794✔
121
                return nil
99✔
122
        }
99✔
123

124
        // Otherwise, we'll check if the account already exists, if so, we can
125
        // once again bail early.
126
        _, err := scope.AccountName(addrmgrNs, uint32(keyFam))
596✔
127
        if err == nil {
1,156✔
128
                return nil
560✔
129
        }
560✔
130

131
        // If we reach this point, then the account hasn't yet been created, so
132
        // we'll need to create it before we can proceed.
133
        return scope.NewRawAccount(addrmgrNs, uint32(keyFam))
36✔
134
}
135

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

331✔
147
        db := b.wallet.Database()
331✔
148
        err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
662✔
149
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
331✔
150

331✔
151
                scope, err := b.keyScope()
331✔
152
                if err != nil {
331✔
153
                        return err
×
154
                }
×
155

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

164
                addrs, err := scope.NextExternalAddresses(
331✔
165
                        addrmgrNs, uint32(keyFam), 1,
331✔
166
                )
331✔
167
                if err != nil {
331✔
168
                        return err
×
169
                }
×
170

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

179
                pubKey = addr.PubKey()
331✔
180

331✔
181
                _, pathInfo, _ := addr.DerivationInfo()
331✔
182
                keyLoc = KeyLocator{
331✔
183
                        Family: keyFam,
331✔
184
                        Index:  pathInfo.Index,
331✔
185
                }
331✔
186

331✔
187
                return nil
331✔
188
        })
189
        if err != nil {
331✔
190
                return KeyDescriptor{}, err
×
191
        }
×
192

193
        return KeyDescriptor{
331✔
194
                PubKey:     pubKey,
331✔
195
                KeyLocator: keyLoc,
331✔
196
        }, nil
331✔
197
}
198

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

280✔
207
        db := b.wallet.Database()
280✔
208
        err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
560✔
209
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
280✔
210

280✔
211
                scope, err := b.keyScope()
280✔
212
                if err != nil {
280✔
213
                        return err
×
214
                }
×
215

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

230
                path := waddrmgr.DerivationPath{
280✔
231
                        InternalAccount: uint32(keyLoc.Family),
280✔
232
                        Branch:          0,
280✔
233
                        Index:           keyLoc.Index,
280✔
234
                }
280✔
235
                addr, err := scope.DeriveFromKeyPath(addrmgrNs, path)
280✔
236
                if err != nil {
280✔
237
                        return err
×
238
                }
×
239

240
                keyDesc.KeyLocator = keyLoc
280✔
241
                keyDesc.PubKey = addr.(waddrmgr.ManagedPubKeyAddress).PubKey()
280✔
242

280✔
243
                return nil
280✔
244
        })
245
        if err != nil {
280✔
246
                return keyDesc, err
×
247
        }
×
248

249
        return keyDesc, nil
280✔
250
}
251

252
// DerivePrivKey attempts to derive the private key that corresponds to the
253
// passed key descriptor.
254
//
255
// NOTE: This is part of the keychain.SecretKeyRing interface.
256
func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (
257
        *btcec.PrivateKey, error) {
104✔
258

104✔
259
        var key *btcec.PrivateKey
104✔
260

104✔
261
        scope, err := b.keyScope()
104✔
262
        if err != nil {
104✔
263
                return nil, err
×
264
        }
×
265

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

282
        db := b.wallet.Database()
84✔
283
        err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
168✔
284
                addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
84✔
285

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

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

316
                        key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
44✔
317
                        if err != nil {
44✔
318
                                return err
×
319
                        }
×
320

321
                        return nil
44✔
322
                }
323

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

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

80✔
349
                        // If this is the target public key, then we'll return
80✔
350
                        // it directly back to the caller.
80✔
351
                        if managedAddr.PubKey().IsEqual(keyDesc.PubKey) {
100✔
352
                                key, err = managedAddr.PrivKey()
20✔
353
                                if err != nil {
20✔
354
                                        return err
×
355
                                }
×
356

357
                                return nil
20✔
358
                        }
359

360
                        // This wasn't the target key, so roll forward and try
361
                        // the next one.
362
                        nextPath.Index++
60✔
363
                }
364

365
                // If we reach this point, then we we're unable to derive the
366
                // private key, so return an error back to the user.
367
                return ErrCannotDerivePrivKey
20✔
368
        })
369
        if err != nil {
104✔
370
                return nil, err
20✔
371
        }
20✔
372

373
        return key, nil
64✔
374
}
375

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

44✔
388
        privKey, err := b.DerivePrivKey(keyDesc)
44✔
389
        if err != nil {
44✔
390
                return [32]byte{}, err
×
391
        }
×
392

393
        var (
44✔
394
                pubJacobian btcec.JacobianPoint
44✔
395
                s           btcec.JacobianPoint
44✔
396
        )
44✔
397
        pub.AsJacobian(&pubJacobian)
44✔
398

44✔
399
        btcec.ScalarMultNonConst(&privKey.Key, &pubJacobian, &s)
44✔
400
        s.ToAffine()
44✔
401
        sPubKey := btcec.NewPublicKey(&s.X, &s.Y)
44✔
402
        h := sha256.Sum256(sPubKey.SerializeCompressed())
44✔
403

44✔
404
        return h, nil
44✔
405
}
406

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

×
UNCOV
414
        privKey, err := b.DerivePrivKey(KeyDescriptor{
×
UNCOV
415
                KeyLocator: keyLoc,
×
UNCOV
416
        })
×
UNCOV
417
        if err != nil {
×
418
                return nil, err
×
419
        }
×
420

UNCOV
421
        var digest []byte
×
UNCOV
422
        if doubleHash {
×
UNCOV
423
                digest = chainhash.DoubleHashB(msg)
×
UNCOV
424
        } else {
×
UNCOV
425
                digest = chainhash.HashB(msg)
×
UNCOV
426
        }
×
UNCOV
427
        return ecdsa.Sign(privKey, digest), nil
×
428
}
429

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

×
UNCOV
438
        privKey, err := b.DerivePrivKey(KeyDescriptor{
×
UNCOV
439
                KeyLocator: keyLoc,
×
UNCOV
440
        })
×
UNCOV
441
        if err != nil {
×
442
                return nil, err
×
443
        }
×
444

UNCOV
445
        var digest []byte
×
UNCOV
446
        if doubleHash {
×
UNCOV
447
                digest = chainhash.DoubleHashB(msg)
×
UNCOV
448
        } else {
×
UNCOV
449
                digest = chainhash.HashB(msg)
×
UNCOV
450
        }
×
451

UNCOV
452
        return ecdsa.SignCompact(privKey, digest, true), nil
×
453
}
454

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

×
UNCOV
465
        privKey, err := b.DerivePrivKey(KeyDescriptor{
×
UNCOV
466
                KeyLocator: keyLoc,
×
UNCOV
467
        })
×
UNCOV
468
        if err != nil {
×
469
                return nil, err
×
470
        }
×
471

UNCOV
472
        if len(taprootTweak) > 0 {
×
UNCOV
473
                privKey = txscript.TweakTaprootPrivKey(*privKey, taprootTweak)
×
UNCOV
474
        }
×
475

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