• 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

45.8
/lnwallet/interface.go
1
package lnwallet
2

3
import (
4
        "errors"
5
        "fmt"
6
        "sync"
7
        "time"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
11
        "github.com/btcsuite/btcd/btcutil"
12
        "github.com/btcsuite/btcd/btcutil/hdkeychain"
13
        "github.com/btcsuite/btcd/btcutil/psbt"
14
        "github.com/btcsuite/btcd/chaincfg"
15
        "github.com/btcsuite/btcd/chaincfg/chainhash"
16
        "github.com/btcsuite/btcd/txscript"
17
        "github.com/btcsuite/btcd/wire"
18
        "github.com/btcsuite/btcwallet/waddrmgr"
19
        base "github.com/btcsuite/btcwallet/wallet"
20
        "github.com/btcsuite/btcwallet/wallet/txauthor"
21
        "github.com/btcsuite/btcwallet/wtxmgr"
22
        "github.com/lightningnetwork/lnd/fn/v2"
23
        "github.com/lightningnetwork/lnd/keychain"
24
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
25
        "github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
26
        "github.com/lightningnetwork/lnd/lnwire"
27
)
28

29
const (
30
        // DefaultAccountName is the name for the default account used to manage
31
        // on-chain funds within the wallet.
32
        DefaultAccountName = "default"
33
)
34

35
// AddressType is an enum-like type which denotes the possible address types
36
// WalletController supports.
37
type AddressType uint8
38

39
// AccountAddressMap maps the account properties to an array of
40
// address properties.
41
type AccountAddressMap map[*waddrmgr.AccountProperties][]AddressProperty
42

43
const (
44
        // UnknownAddressType represents an output with an unknown or non-standard
45
        // script.
46
        UnknownAddressType AddressType = iota
47

48
        // WitnessPubKey represents a p2wkh address.
49
        WitnessPubKey
50

51
        // NestedWitnessPubKey represents a p2sh output which is itself a
52
        // nested p2wkh output.
53
        NestedWitnessPubKey
54

55
        // TaprootPubkey represents a p2tr key path spending address.
56
        TaprootPubkey
57
)
58

59
var (
60
        // DefaultPublicPassphrase is the default public passphrase used for the
61
        // wallet.
62
        DefaultPublicPassphrase = []byte("public")
63

64
        // DefaultPrivatePassphrase is the default private passphrase used for
65
        // the wallet.
66
        DefaultPrivatePassphrase = []byte("hello")
67

68
        // ErrDoubleSpend is returned from PublishTransaction in case the
69
        // tx being published is spending an output spent by a conflicting
70
        // transaction.
71
        ErrDoubleSpend = errors.New("transaction rejected: output already spent")
72

73
        // ErrNotMine is an error denoting that a WalletController instance is
74
        // unable to spend a specified output.
75
        ErrNotMine = errors.New("the passed output doesn't belong to the wallet")
76

77
        // ErrMempoolFee is returned from PublishTransaction in case the tx
78
        // being published is not accepted into mempool because the fee
79
        // requirements of the mempool backend are not met.
80
        ErrMempoolFee = errors.New("transaction rejected by the mempool " +
81
                "because of low fees")
82
)
83

84
// ErrNoOutputs is returned if we try to create a transaction with no outputs
85
// or send coins to a set of outputs that is empty.
86
var ErrNoOutputs = errors.New("no outputs")
87

88
// ErrInvalidMinconf is returned if we try to create a transaction with
89
// invalid minConfs value.
90
var ErrInvalidMinconf = errors.New("minimum number of confirmations must " +
91
        "be a non-negative number")
92

93
// AddressProperty contains wallet related information of an address.
94
type AddressProperty struct {
95
        // Address is the address of an account.
96
        Address string
97

98
        // Internal denotes if the address is a change address.
99
        Internal bool
100

101
        // Balance returns the total balance of an address.
102
        Balance btcutil.Amount
103

104
        // DerivationPath is the derivation path of the address.
105
        DerivationPath string
106

107
        // PublicKey is the public key of the address.
108
        PublicKey *btcec.PublicKey
109
}
110

111
// AccountIdentifier contains information to uniquely identify an account.
112
type AccountIdentifier struct {
113
        // Name is the name of the account.
114
        Name string
115

116
        // AddressType is the type of addresses supported by the account.
117
        AddressType AddressType
118

119
        // DerivationPath is the derivation path corresponding to the account
120
        // public key.
121
        DerivationPath string
122
}
123

124
// Utxo is an unspent output denoted by its outpoint, and output value of the
125
// original output.
126
type Utxo struct {
127
        AddressType   AddressType
128
        Value         btcutil.Amount
129
        Confirmations int64
130
        PkScript      []byte
131
        wire.OutPoint
132
        PrevTx *wire.MsgTx
133
}
134

135
// OutputDetail contains additional information on a destination address.
136
type OutputDetail struct {
137
        OutputType   txscript.ScriptClass
138
        Addresses    []btcutil.Address
139
        PkScript     []byte
140
        OutputIndex  int
141
        Value        btcutil.Amount
142
        IsOurAddress bool
143
}
144

145
// PreviousOutPoint contains information about the previous outpoint.
146
type PreviousOutPoint struct {
147
        // OutPoint is the transaction out point in the format txid:n.
148
        OutPoint string
149

150
        // IsOurOutput denotes if the previous output is controlled by the
151
        // internal wallet. The flag will only detect p2wkh, np2wkh and p2tr
152
        // inputs as its own.
153
        IsOurOutput bool
154
}
155

156
// TransactionDetail describes a transaction with either inputs which belong to
157
// the wallet, or has outputs that pay to the wallet.
158
type TransactionDetail struct {
159
        // Hash is the transaction hash of the transaction.
160
        Hash chainhash.Hash
161

162
        // Value is the net value of this transaction (in satoshis) from the
163
        // PoV of the wallet. If this transaction purely spends from the
164
        // wallet's funds, then this value will be negative. Similarly, if this
165
        // transaction credits the wallet, then this value will be positive.
166
        Value btcutil.Amount
167

168
        // NumConfirmations is the number of confirmations this transaction
169
        // has. If the transaction is unconfirmed, then this value will be
170
        // zero.
171
        NumConfirmations int32
172

173
        // BlockHeight is the hash of the block which includes this
174
        // transaction. Unconfirmed transactions will have a nil value for this
175
        // field.
176
        BlockHash *chainhash.Hash
177

178
        // BlockHeight is the height of the block including this transaction.
179
        // Unconfirmed transaction will show a height of zero.
180
        BlockHeight int32
181

182
        // Timestamp is the unix timestamp of the block including this
183
        // transaction. If the transaction is unconfirmed, then this will be a
184
        // timestamp of txn creation.
185
        Timestamp int64
186

187
        // TotalFees is the total fee in satoshis paid by this transaction.
188
        TotalFees int64
189

190
        // OutputDetails contains output data for each destination address, such
191
        // as the output script and amount.
192
        OutputDetails []OutputDetail
193

194
        // RawTx returns the raw serialized transaction.
195
        RawTx []byte
196

197
        // Label is an optional transaction label.
198
        Label string
199

200
        // PreviousOutpoints are the inputs for a transaction.
201
        PreviousOutpoints []PreviousOutPoint
202
}
203

204
// TransactionSubscription is an interface which describes an object capable of
205
// receiving notifications of new transaction related to the underlying wallet.
206
// TODO(roasbeef): add balance updates?
207
type TransactionSubscription interface {
208
        // ConfirmedTransactions returns a channel which will be sent on as new
209
        // relevant transactions are confirmed.
210
        ConfirmedTransactions() chan *TransactionDetail
211

212
        // UnconfirmedTransactions returns a channel which will be sent on as
213
        // new relevant transactions are seen within the network.
214
        UnconfirmedTransactions() chan *TransactionDetail
215

216
        // Cancel finalizes the subscription, cleaning up any resources
217
        // allocated.
218
        Cancel()
219
}
220

221
// WalletController defines an abstract interface for controlling a local Pure
222
// Go wallet, a local or remote wallet via an RPC mechanism, or possibly even
223
// a daemon assisted hardware wallet. This interface serves the purpose of
224
// allowing LightningWallet to be seamlessly compatible with several wallets
225
// such as: uspv, btcwallet, Bitcoin Core, Electrum, etc. This interface then
226
// serves as a "base wallet", with Lightning Network awareness taking place at
227
// a "higher" level of abstraction. Essentially, an overlay wallet.
228
// Implementors of this interface must closely adhere to the documented
229
// behavior of all interface methods in order to ensure identical behavior
230
// across all concrete implementations.
231
type WalletController interface {
232
        // FetchOutpointInfo queries for the WalletController's knowledge of
233
        // the passed outpoint. If the base wallet determines this output is
234
        // under its control, then the original txout should be returned.
235
        // Otherwise, a non-nil error value of ErrNotMine should be returned
236
        // instead.
237
        FetchOutpointInfo(prevOut *wire.OutPoint) (*Utxo, error)
238

239
        // FetchDerivationInfo queries for the wallet's knowledge of the passed
240
        // pkScript and constructs the derivation info and returns it.
241
        FetchDerivationInfo(pkScript []byte) (*psbt.Bip32Derivation, error)
242

243
        // ScriptForOutput returns the address, witness program and redeem
244
        // script for a given UTXO. An error is returned if the UTXO does not
245
        // belong to our wallet or it is not a managed pubKey address.
246
        ScriptForOutput(output *wire.TxOut) (waddrmgr.ManagedPubKeyAddress,
247
                []byte, []byte, error)
248

249
        // ConfirmedBalance returns the sum of all the wallet's unspent outputs
250
        // that have at least confs confirmations. If confs is set to zero,
251
        // then all unspent outputs, including those currently in the mempool
252
        // will be included in the final sum. The account parameter serves as a
253
        // filter to retrieve the balance for a specific account. When empty,
254
        // the confirmed balance of all wallet accounts is returned.
255
        //
256
        // NOTE: Only witness outputs should be included in the computation of
257
        // the total spendable balance of the wallet. We require this as only
258
        // witness inputs can be used for funding channels.
259
        ConfirmedBalance(confs int32, accountFilter string) (btcutil.Amount,
260
                error)
261

262
        // NewAddress returns the next external or internal address for the
263
        // wallet dictated by the value of the `change` parameter. If change is
264
        // true, then an internal address should be used, otherwise an external
265
        // address should be returned. The type of address returned is dictated
266
        // by the wallet's capabilities, and may be of type: p2sh, p2wkh,
267
        // p2wsh, etc. The account parameter must be non-empty as it determines
268
        // which account the address should be generated from.
269
        NewAddress(addrType AddressType, change bool,
270
                account string) (btcutil.Address, error)
271

272
        // LastUnusedAddress returns the last *unused* address known by the
273
        // wallet. An address is unused if it hasn't received any payments.
274
        // This can be useful in UIs in order to continually show the
275
        // "freshest" address without having to worry about "address inflation"
276
        // caused by continual refreshing. Similar to NewAddress it can derive
277
        // a specified address type. By default, this is a non-change address.
278
        // The account parameter must be non-empty as it determines which
279
        // account the address should be generated from.
280
        LastUnusedAddress(addrType AddressType,
281
                account string) (btcutil.Address, error)
282

283
        // IsOurAddress checks if the passed address belongs to this wallet
284
        IsOurAddress(a btcutil.Address) bool
285

286
        // AddressInfo returns the information about an address, if it's known
287
        // to this wallet.
288
        AddressInfo(a btcutil.Address) (waddrmgr.ManagedAddress, error)
289

290
        // ListAccounts retrieves all accounts belonging to the wallet by
291
        // default. A name and key scope filter can be provided to filter
292
        // through all of the wallet accounts and return only those matching.
293
        ListAccounts(string, *waddrmgr.KeyScope) ([]*waddrmgr.AccountProperties,
294
                error)
295

296
        // RequiredReserve returns the minimum amount of satoshis that should be
297
        // kept in the wallet in order to fee bump anchor channels if necessary.
298
        // The value scales with the number of public anchor channels but is
299
        // capped at a maximum.
300
        RequiredReserve(uint32) btcutil.Amount
301

302
        // ListAddresses retrieves all the addresses along with their balance. An
303
        // account name filter can be provided to filter through all of the
304
        // wallet accounts and return the addresses of only those matching.
305
        ListAddresses(string, bool) (AccountAddressMap, error)
306

307
        // ImportAccount imports an account backed by an account extended public
308
        // key. The master key fingerprint denotes the fingerprint of the root
309
        // key corresponding to the account public key (also known as the key
310
        // with derivation path m/). This may be required by some hardware
311
        // wallets for proper identification and signing.
312
        //
313
        // The address type can usually be inferred from the key's version, but
314
        // may be required for certain keys to map them into the proper scope.
315
        //
316
        // For BIP-0044 keys, an address type must be specified as we intend to
317
        // not support importing BIP-0044 keys into the wallet using the legacy
318
        // pay-to-pubkey-hash (P2PKH) scheme. A nested witness address type will
319
        // force the standard BIP-0049 derivation scheme, while a witness
320
        // address type will force the standard BIP-0084 derivation scheme.
321
        //
322
        // For BIP-0049 keys, an address type must also be specified to make a
323
        // distinction between the standard BIP-0049 address schema (nested
324
        // witness pubkeys everywhere) and our own BIP-0049Plus address schema
325
        // (nested pubkeys externally, witness pubkeys internally).
326
        ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey,
327
                masterKeyFingerprint uint32, addrType *waddrmgr.AddressType,
328
                dryRun bool) (*waddrmgr.AccountProperties, []btcutil.Address,
329
                []btcutil.Address, error)
330

331
        // ImportPublicKey imports a single derived public key into the wallet.
332
        // The address type can usually be inferred from the key's version, but
333
        // in the case of legacy versions (xpub, tpub), an address type must be
334
        // specified as we intend to not support importing BIP-44 keys into the
335
        // wallet using the legacy pay-to-pubkey-hash (P2PKH) scheme.
336
        ImportPublicKey(pubKey *btcec.PublicKey,
337
                addrType waddrmgr.AddressType) error
338

339
        // ImportTaprootScript imports a user-provided taproot script into the
340
        // wallet. The imported script will act as a pay-to-taproot address.
341
        //
342
        // NOTE: Taproot keys imported through this RPC currently _cannot_ be
343
        // used for funding PSBTs. Only tracking the balance and UTXOs is
344
        // currently supported.
345
        ImportTaprootScript(scope waddrmgr.KeyScope,
346
                tapscript *waddrmgr.Tapscript) (waddrmgr.ManagedAddress, error)
347

348
        // SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying
349
        // out to the specified outputs. In the case the wallet has insufficient
350
        // funds, or the outputs are non-standard, an error should be returned.
351
        // This method also takes the target fee expressed in sat/kw that should
352
        // be used when crafting the transaction.
353
        //
354
        // NOTE: This method requires the global coin selection lock to be held.
355
        SendOutputs(inputs fn.Set[wire.OutPoint], outputs []*wire.TxOut,
356
                feeRate chainfee.SatPerKWeight, minConfs int32, label string,
357
                strategy base.CoinSelectionStrategy) (*wire.MsgTx, error)
358

359
        // CreateSimpleTx creates a Bitcoin transaction paying to the specified
360
        // outputs. The transaction is not broadcasted to the network. In the
361
        // case the wallet has insufficient funds, or the outputs are
362
        // non-standard, an error should be returned. This method also takes
363
        // the target fee expressed in sat/kw that should be used when crafting
364
        // the transaction.
365
        //
366
        // NOTE: The dryRun argument can be set true to create a tx that
367
        // doesn't alter the database. A tx created with this set to true
368
        // SHOULD NOT be broadcasted.
369
        //
370
        // NOTE: This method requires the global coin selection lock to be held.
371
        CreateSimpleTx(inputs fn.Set[wire.OutPoint], outputs []*wire.TxOut,
372
                feeRate chainfee.SatPerKWeight, minConfs int32,
373
                strategy base.CoinSelectionStrategy, dryRun bool) (
374
                *txauthor.AuthoredTx, error)
375

376
        // GetTransactionDetails returns a detailed description of a transaction
377
        // given its transaction hash.
378
        GetTransactionDetails(txHash *chainhash.Hash) (
379
                *TransactionDetail, error)
380

381
        // ListUnspentWitness returns all unspent outputs which are version 0
382
        // witness programs. The 'minConfs' and 'maxConfs' parameters
383
        // indicate the minimum and maximum number of confirmations an output
384
        // needs in order to be returned by this method. Passing -1 as
385
        // 'minConfs' indicates that even unconfirmed outputs should be
386
        // returned. Using MaxInt32 as 'maxConfs' implies returning all
387
        // outputs with at least 'minConfs'. The account parameter serves as
388
        // a filter to retrieve the unspent outputs for a specific account.
389
        // When empty, the unspent outputs of all wallet accounts are returned.
390
        //
391
        // NOTE: This method requires the global coin selection lock to be held.
392
        ListUnspentWitness(minConfs, maxConfs int32,
393
                accountFilter string) ([]*Utxo, error)
394

395
        // ListTransactionDetails returns a list of all transactions which are
396
        // relevant to the wallet over [startHeight;endHeight]. If start height
397
        // is greater than end height, the transactions will be retrieved in
398
        // reverse order. To include unconfirmed transactions, endHeight should
399
        // be set to the special value -1. This will return transactions from
400
        // the tip of the chain until the start height (inclusive) and
401
        // unconfirmed transactions. The account parameter serves as a filter to
402
        // retrieve the transactions relevant to a specific account. When
403
        // empty, transactions of all wallet accounts are returned.
404
        ListTransactionDetails(startHeight, endHeight int32,
405
                accountFilter string, indexOffset uint32,
406
                maxTransactions uint32) ([]*TransactionDetail, uint64, uint64,
407
                error)
408

409
        // LeaseOutput locks an output to the given ID, preventing it from being
410
        // available for any future coin selection attempts. The absolute time
411
        // of the lock's expiration is returned. The expiration of the lock can
412
        // be extended by successive invocations of this call. Outputs can be
413
        // unlocked before their expiration through `ReleaseOutput`.
414
        //
415
        // If the output is not known, wtxmgr.ErrUnknownOutput is returned. If
416
        // the output has already been locked to a different ID, then
417
        // wtxmgr.ErrOutputAlreadyLocked is returned.
418
        //
419
        // NOTE: This method requires the global coin selection lock to be held.
420
        LeaseOutput(id wtxmgr.LockID, op wire.OutPoint,
421
                duration time.Duration) (time.Time, error)
422

423
        // ReleaseOutput unlocks an output, allowing it to be available for coin
424
        // selection if it remains unspent. The ID should match the one used to
425
        // originally lock the output.
426
        //
427
        // NOTE: This method requires the global coin selection lock to be held.
428
        ReleaseOutput(id wtxmgr.LockID, op wire.OutPoint) error
429

430
        // ListLeasedOutputs returns a list of all currently locked outputs.
431
        ListLeasedOutputs() ([]*base.ListLeasedOutputResult, error)
432

433
        // PublishTransaction performs cursory validation (dust checks, etc),
434
        // then finally broadcasts the passed transaction to the Bitcoin network.
435
        // If the transaction is rejected because it is conflicting with an
436
        // already known transaction, ErrDoubleSpend is returned. If the
437
        // transaction is already known (published already), no error will be
438
        // returned. Other error returned depends on the currently active chain
439
        // backend. It takes an optional label which will save a label with the
440
        // published transaction.
441
        PublishTransaction(tx *wire.MsgTx, label string) error
442

443
        // LabelTransaction adds a label to a transaction. If the tx already
444
        // has a label, this call will fail unless the overwrite parameter
445
        // is set. Labels must not be empty, and they are limited to 500 chars.
446
        LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error
447

448
        // FetchTx attempts to fetch a transaction in the wallet's database
449
        // identified by the passed transaction hash. If the transaction can't
450
        // be found, then a nil pointer is returned.
451
        FetchTx(chainhash.Hash) (*wire.MsgTx, error)
452

453
        // RemoveDescendants attempts to remove any transaction from the
454
        // wallet's tx store (that may be unconfirmed) that spends outputs
455
        // created by the passed transaction. This remove propagates
456
        // recursively down the chain of descendent transactions.
457
        RemoveDescendants(*wire.MsgTx) error
458

459
        // FundPsbt creates a fully populated PSBT packet that contains enough
460
        // inputs to fund the outputs specified in the passed in packet with the
461
        // specified fee rate. If there is change left, a change output from the
462
        // internal wallet is added and the index of the change output is
463
        // returned. Otherwise no additional output is created and the index -1
464
        // is returned. If no custom change scope is specified, the BIP0084 will
465
        // be used for default accounts and single imported public keys. For
466
        // custom account, no key scope should be provided as the coin selection
467
        // key scope will always be used to generate the change address.
468
        //
469
        // NOTE: If the packet doesn't contain any inputs, coin selection is
470
        // performed automatically. The account parameter must be non-empty as
471
        // it determines which set of coins are eligible for coin selection. If
472
        // the packet does contain any inputs, it is assumed that full coin
473
        // selection happened externally and no additional inputs are added. If
474
        // the specified inputs aren't enough to fund the outputs with the given
475
        // fee rate, an error is returned. No lock lease is acquired for any of
476
        // the selected/validated inputs. It is in the caller's responsibility
477
        // to lock the inputs before handing them out.
478
        FundPsbt(packet *psbt.Packet, minConfs int32,
479
                feeRate chainfee.SatPerKWeight, account string,
480
                changeScope *waddrmgr.KeyScope,
481
                strategy base.CoinSelectionStrategy,
482
                allowUtxo func(wtxmgr.Credit) bool) (int32, error)
483

484
        // SignPsbt expects a partial transaction with all inputs and outputs
485
        // fully declared and tries to sign all unsigned inputs that have all
486
        // required fields (UTXO information, BIP32 derivation information,
487
        // witness or sig scripts) set.
488
        // If no error is returned, the PSBT is ready to be given to the next
489
        // signer or to be finalized if lnd was the last signer.
490
        //
491
        // NOTE: This method only signs inputs (and only those it can sign), it
492
        // does not perform any other tasks (such as coin selection, UTXO
493
        // locking or input/output/fee value validation, PSBT finalization). Any
494
        // input that is incomplete will be skipped.
495
        SignPsbt(packet *psbt.Packet) ([]uint32, error)
496

497
        // FinalizePsbt expects a partial transaction with all inputs and
498
        // outputs fully declared and tries to sign all inputs that belong to
499
        // the specified account. Lnd must be the last signer of the
500
        // transaction. That means, if there are any unsigned non-witness inputs
501
        // or inputs without UTXO information attached or inputs without witness
502
        // data that do not belong to lnd's wallet, this method will fail. If no
503
        // error is returned, the PSBT is ready to be extracted and the final TX
504
        // within to be broadcast.
505
        //
506
        // NOTE: This method does NOT publish the transaction after it's been
507
        // finalized successfully.
508
        FinalizePsbt(packet *psbt.Packet, account string) error
509

510
        // DecorateInputs fetches the UTXO information of all inputs it can
511
        // identify and adds the required information to the package's inputs.
512
        // The failOnUnknown boolean controls whether the method should return
513
        // an error if it cannot identify an input or if it should just skip it.
514
        DecorateInputs(packet *psbt.Packet, failOnUnknown bool) error
515

516
        // SubscribeTransactions returns a TransactionSubscription client which
517
        // is capable of receiving async notifications as new transactions
518
        // related to the wallet are seen within the network, or found in
519
        // blocks.
520
        //
521
        // NOTE: a non-nil error should be returned if notifications aren't
522
        // supported.
523
        //
524
        // TODO(roasbeef): make distinct interface?
525
        SubscribeTransactions() (TransactionSubscription, error)
526

527
        // IsSynced returns a boolean indicating if from the PoV of the wallet,
528
        // it has fully synced to the current best block in the main chain.
529
        // It also returns an int64 indicating the timestamp of the best block
530
        // known to the wallet, expressed in Unix epoch time
531
        IsSynced() (bool, int64, error)
532

533
        // GetRecoveryInfo returns a boolean indicating whether the wallet is
534
        // started in recovery mode. It also returns a float64 indicating the
535
        // recovery progress made so far.
536
        GetRecoveryInfo() (bool, float64, error)
537

538
        // Start initializes the wallet, making any necessary connections,
539
        // starting up required goroutines etc.
540
        Start() error
541

542
        // Stop signals the wallet for shutdown. Shutdown may entail closing
543
        // any active sockets, database handles, stopping goroutines, etc.
544
        Stop() error
545

546
        // BackEnd returns a name for the wallet's backing chain service,
547
        // which could be e.g. btcd, bitcoind, neutrino, or another consensus
548
        // service.
549
        BackEnd() string
550

551
        // CheckMempoolAcceptance checks whether a transaction follows mempool
552
        // policies and returns an error if it cannot be accepted into the
553
        // mempool.
554
        CheckMempoolAcceptance(tx *wire.MsgTx) error
555
}
556

557
// BlockChainIO is a dedicated source which will be used to obtain queries
558
// related to the current state of the blockchain. The data returned by each of
559
// the defined methods within this interface should always return the most up
560
// to date data possible.
561
//
562
// TODO(roasbeef): move to diff package perhaps?
563
// TODO(roasbeef): move publish txn here?
564
type BlockChainIO interface {
565
        // GetBestBlock returns the current height and block hash of the valid
566
        // most-work chain the implementation is aware of.
567
        GetBestBlock() (*chainhash.Hash, int32, error)
568

569
        // GetUtxo attempts to return the passed outpoint if it's still a
570
        // member of the utxo set. The passed height hint should be the "birth
571
        // height" of the passed outpoint. The script passed should be the
572
        // script that the outpoint creates. In the case that the output is in
573
        // the UTXO set, then the output corresponding to that output is
574
        // returned.  Otherwise, a non-nil error will be returned.
575
        // As for some backends this call can initiate a rescan, the passed
576
        // cancel channel can be closed to abort the call.
577
        GetUtxo(op *wire.OutPoint, pkScript []byte, heightHint uint32,
578
                cancel <-chan struct{}) (*wire.TxOut, error)
579

580
        // GetBlockHash returns the hash of the block in the best blockchain
581
        // at the given height.
582
        GetBlockHash(blockHeight int64) (*chainhash.Hash, error)
583

584
        // GetBlock returns the block in the main chain identified by the given
585
        // hash.
586
        GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error)
587

588
        // GetBlockHeader returns the block header for the given block hash.
589
        GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, error)
590
}
591

592
// MessageSigner represents an abstract object capable of signing arbitrary
593
// messages. The capabilities of this interface are used to sign announcements
594
// to the network, or just arbitrary messages that leverage the wallet's keys
595
// to attest to some message.
596
type MessageSigner interface {
597
        // SignMessage attempts to sign a target message with the private key
598
        // described in the key locator. If the target private key is unable to
599
        // be found, then an error will be returned. The actual digest signed is
600
        // the single or double SHA-256 of the passed message.
601
        SignMessage(keyLoc keychain.KeyLocator, msg []byte,
602
                doubleHash bool) (*ecdsa.Signature, error)
603
}
604

605
// AddrWithKey wraps a normal addr, but also includes the internal key for the
606
// delivery addr if known.
607
type AddrWithKey struct {
608
        lnwire.DeliveryAddress
609

610
        InternalKey fn.Option[keychain.KeyDescriptor]
611

612
        // TODO(roasbeef): consolidate w/ instance in chan closer
613
}
614

615
// InternalKeyForAddr returns the internal key associated with a taproot
616
// address.
617
func InternalKeyForAddr(wallet WalletController, netParams *chaincfg.Params,
618
        deliveryScript []byte) (fn.Option[keychain.KeyDescriptor], error) {
9✔
619

9✔
620
        none := fn.None[keychain.KeyDescriptor]()
9✔
621

9✔
622
        pkScript, err := txscript.ParsePkScript(deliveryScript)
9✔
623
        if err != nil {
9✔
624
                return none, err
×
625
        }
×
626
        addr, err := pkScript.Address(netParams)
9✔
627
        if err != nil {
9✔
628
                return none, err
×
629
        }
×
630

631
        // If it's not a taproot address, we don't require to know the internal
632
        // key in the first place. So we don't return an error here, but also no
633
        // internal key.
634
        _, isTaproot := addr.(*btcutil.AddressTaproot)
9✔
635
        if !isTaproot {
18✔
636
                return none, nil
9✔
637
        }
9✔
638

UNCOV
639
        walletAddr, err := wallet.AddressInfo(addr)
×
UNCOV
640
        if err != nil {
×
UNCOV
641
                // If the error is that the address can't be found, it is not
×
UNCOV
642
                // an error. This happens when any channel which is not a custom
×
UNCOV
643
                // taproot channel is cooperatively closed to an external P2TR
×
UNCOV
644
                // address. In this case there is no internal key associated
×
UNCOV
645
                // with the address. Callers can use the .Option() method to get
×
UNCOV
646
                // an option value.
×
UNCOV
647
                var managerErr waddrmgr.ManagerError
×
UNCOV
648
                if errors.As(err, &managerErr) &&
×
UNCOV
649
                        managerErr.ErrorCode == waddrmgr.ErrAddressNotFound {
×
UNCOV
650

×
UNCOV
651
                        return none, nil
×
UNCOV
652
                }
×
653

654
                return none, err
×
655
        }
656

657
        // No wallet addr. No error, but we'll return an nil error value here,
658
        // as callers can use the .Option() method to get an option value.
UNCOV
659
        if walletAddr == nil {
×
660
                return none, nil
×
661
        }
×
662

UNCOV
663
        pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress)
×
UNCOV
664
        if !ok {
×
665
                return none, fmt.Errorf("expected pubkey addr, got %T",
×
666
                        pubKeyAddr)
×
667
        }
×
668

UNCOV
669
        _, derivationPath, _ := pubKeyAddr.DerivationInfo()
×
UNCOV
670

×
UNCOV
671
        return fn.Some[keychain.KeyDescriptor](keychain.KeyDescriptor{
×
UNCOV
672
                KeyLocator: keychain.KeyLocator{
×
UNCOV
673
                        Family: keychain.KeyFamily(derivationPath.Account),
×
UNCOV
674
                        Index:  derivationPath.Index,
×
UNCOV
675
                },
×
UNCOV
676
                PubKey: pubKeyAddr.PubKey(),
×
UNCOV
677
        }), nil
×
678
}
679

680
// WalletDriver represents a "driver" for a particular concrete
681
// WalletController implementation. A driver is identified by a globally unique
682
// string identifier along with a 'New()' method which is responsible for
683
// initializing a particular WalletController concrete implementation.
684
type WalletDriver struct {
685
        // WalletType is a string which uniquely identifies the
686
        // WalletController that this driver, drives.
687
        WalletType string
688

689
        // New creates a new instance of a concrete WalletController
690
        // implementation given a variadic set up arguments. The function takes
691
        // a variadic number of interface parameters in order to provide
692
        // initialization flexibility, thereby accommodating several potential
693
        // WalletController implementations.
694
        New func(args ...interface{}) (WalletController, error)
695

696
        // BackEnds returns a list of available chain service drivers for the
697
        // wallet driver. This could be e.g. bitcoind, btcd, neutrino, etc.
698
        BackEnds func() []string
699
}
700

701
var (
702
        wallets     = make(map[string]*WalletDriver)
703
        registerMtx sync.Mutex
704
)
705

706
// RegisteredWallets returns a slice of all currently registered notifiers.
707
//
708
// NOTE: This function is safe for concurrent access.
709
func RegisteredWallets() []*WalletDriver {
4✔
710
        registerMtx.Lock()
4✔
711
        defer registerMtx.Unlock()
4✔
712

4✔
713
        registeredWallets := make([]*WalletDriver, 0, len(wallets))
4✔
714
        for _, wallet := range wallets {
8✔
715
                registeredWallets = append(registeredWallets, wallet)
4✔
716
        }
4✔
717

718
        return registeredWallets
4✔
719
}
720

721
// RegisterWallet registers a WalletDriver which is capable of driving a
722
// concrete WalletController interface. In the case that this driver has
723
// already been registered, an error is returned.
724
//
725
// NOTE: This function is safe for concurrent access.
726
func RegisterWallet(driver *WalletDriver) error {
18✔
727
        registerMtx.Lock()
18✔
728
        defer registerMtx.Unlock()
18✔
729

18✔
730
        if _, ok := wallets[driver.WalletType]; ok {
18✔
731
                return fmt.Errorf("wallet already registered")
×
732
        }
×
733

734
        wallets[driver.WalletType] = driver
18✔
735

18✔
736
        return nil
18✔
737
}
738

739
// SupportedWallets returns a slice of strings that represents the wallet
740
// drivers that have been registered and are therefore supported.
741
//
742
// NOTE: This function is safe for concurrent access.
743
func SupportedWallets() []string {
×
744
        registerMtx.Lock()
×
745
        defer registerMtx.Unlock()
×
746

×
747
        supportedWallets := make([]string, 0, len(wallets))
×
748
        for walletName := range wallets {
×
749
                supportedWallets = append(supportedWallets, walletName)
×
750
        }
×
751

752
        return supportedWallets
×
753
}
754

755
// FetchFundingTxWrapper is a wrapper around FetchFundingTx, except that it will
756
// exit when the supplied quit channel is closed.
757
func FetchFundingTxWrapper(chain BlockChainIO, chanID *lnwire.ShortChannelID,
758
        quit chan struct{}) (*wire.MsgTx, error) {
17✔
759

17✔
760
        txChan := make(chan *wire.MsgTx, 1)
17✔
761
        errChan := make(chan error, 1)
17✔
762

17✔
763
        go func() {
34✔
764
                tx, err := FetchFundingTx(chain, chanID)
17✔
765
                if err != nil {
18✔
766
                        errChan <- err
1✔
767
                        return
1✔
768
                }
1✔
769

770
                txChan <- tx
16✔
771
        }()
772

773
        select {
17✔
774
        case tx := <-txChan:
16✔
775
                return tx, nil
16✔
776

777
        case err := <-errChan:
1✔
778
                return nil, err
1✔
779

780
        case <-quit:
×
781
                return nil, fmt.Errorf("quit channel passed to " +
×
782
                        "lnwallet.FetchFundingTxWrapper has been closed")
×
783
        }
784
}
785

786
// FetchFundingTx uses the given BlockChainIO to fetch and return the funding
787
// transaction identified by the passed short channel ID.
788
//
789
// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to
790
// later use getblocktxn).
791
func FetchFundingTx(chain BlockChainIO,
792
        chanID *lnwire.ShortChannelID) (*wire.MsgTx, error) {
17✔
793

17✔
794
        // First fetch the block hash by the block number encoded, then use
17✔
795
        // that hash to fetch the block itself.
17✔
796
        blockNum := int64(chanID.BlockHeight)
17✔
797
        blockHash, err := chain.GetBlockHash(blockNum)
17✔
798
        if err != nil {
18✔
799
                return nil, err
1✔
800
        }
1✔
801

802
        fundingBlock, err := chain.GetBlock(blockHash)
16✔
803
        if err != nil {
16✔
804
                return nil, err
×
805
        }
×
806

807
        // As a sanity check, ensure that the advertised transaction index is
808
        // within the bounds of the total number of transactions within a
809
        // block.
810
        numTxns := uint32(len(fundingBlock.Transactions))
16✔
811
        if chanID.TxIndex > numTxns-1 {
16✔
812
                return nil, fmt.Errorf("tx_index=#%v "+
×
813
                        "is out of range (max_index=%v), network_chan_id=%v",
×
814
                        chanID.TxIndex, numTxns-1, chanID)
×
815
        }
×
816

817
        return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil
16✔
818
}
819

820
// FetchPKScriptWithQuit fetches the output script for the given SCID and exits
821
// early with an error if the provided quit channel is closed before
822
// completion.
823
func FetchPKScriptWithQuit(chain BlockChainIO, chanID *lnwire.ShortChannelID,
824
        quit chan struct{}) ([]byte, error) {
×
825

×
826
        tx, err := FetchFundingTxWrapper(chain, chanID, quit)
×
827
        if err != nil {
×
828
                return nil, err
×
829
        }
×
830

831
        outputLocator := chanvalidate.ShortChanIDChanLocator{
×
832
                ID: *chanID,
×
833
        }
×
834

×
835
        output, _, err := outputLocator.Locate(tx)
×
836
        if err != nil {
×
837
                return nil, err
×
838
        }
×
839

840
        return output.PkScript, nil
×
841
}
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