1 package wallet
2 3 import (
4 "bytes"
5 "encoding/hex"
6 "errors"
7 "fmt"
8 "sort"
9 "strings"
10 "sync"
11 "time"
12 13 "github.com/p9c/p9/pkg/qu"
14 15 "github.com/davecgh/go-spew/spew"
16 17 "github.com/p9c/p9/pkg/amt"
18 "github.com/p9c/p9/pkg/btcaddr"
19 "github.com/p9c/p9/pkg/chaincfg"
20 "github.com/p9c/p9/pod/config"
21 22 "github.com/p9c/p9/pkg/blockchain"
23 "github.com/p9c/p9/pkg/btcjson"
24 "github.com/p9c/p9/pkg/chainclient"
25 "github.com/p9c/p9/pkg/chainhash"
26 ec "github.com/p9c/p9/pkg/ecc"
27 "github.com/p9c/p9/pkg/rpcclient"
28 "github.com/p9c/p9/pkg/txauthor"
29 "github.com/p9c/p9/pkg/txrules"
30 "github.com/p9c/p9/pkg/txscript"
31 "github.com/p9c/p9/pkg/util"
32 "github.com/p9c/p9/pkg/util/hdkeychain"
33 "github.com/p9c/p9/pkg/waddrmgr"
34 "github.com/p9c/p9/pkg/walletdb"
35 "github.com/p9c/p9/pkg/wire"
36 "github.com/p9c/p9/pkg/wtxmgr"
37 )
38 39 const (
40 // InsecurePubPassphrase is the default outer encryption passphrase used for public data (everything but private
41 // keys). Using a non-default public passphrase can prevent an attacker without the public passphrase from
42 // discovering all past and future wallet addresses if they gain access to the wallet database.
43 //
44 // NOTE: at time of writing, public encryption only applies to public data in the waddrmgr namespace. Transactions
45 // are not yet encrypted.
46 InsecurePubPassphrase = ""
47 // walletDbWatchingOnlyName = "wowallet.db" recoveryBatchSize is the default number of blocks that will be scanned
48 // successively by the recovery manager, in the event that the wallet is started in recovery mode.
49 recoveryBatchSize = 2000
50 )
51 52 // ErrNotSynced describes an error where an operation cannot complete due wallet being out of sync (and perhaps
53 // currently syncing with) the remote chain server.
54 var ErrNotSynced = errors.New("wallet is not synchronized with the chain server")
55 56 // Namespace bucket keys.
57 var (
58 waddrmgrNamespaceKey = []byte("waddrmgr")
59 wtxmgrNamespaceKey = []byte("wtxmgr")
60 )
61 62 // Wallet is a structure containing all the components for a complete wallet. It contains the Armory-style key store
63 // addresses and keys),
64 type Wallet struct {
65 publicPassphrase []byte
66 // Data stores
67 db walletdb.DB
68 Manager *waddrmgr.Manager
69 TxStore *wtxmgr.Store
70 chainClient chainclient.Interface
71 chainClientLock sync.Mutex
72 chainClientSynced bool
73 chainClientSyncMtx sync.Mutex
74 lockedOutpoints map[wire.OutPoint]struct{}
75 recoveryWindow uint32
76 // Channels for rescan processing. Requests are added and merged with any waiting requests, before being sent to
77 // another goroutine to call the rescan RPC.
78 rescanAddJob chan *RescanJob
79 rescanBatch chan *rescanBatch
80 rescanNotifications chan interface{} // From chain server
81 rescanProgress chan *RescanProgressMsg
82 rescanFinished chan *RescanFinishedMsg
83 // Channel for transaction creation requests.
84 createTxRequests chan createTxRequest
85 // Channels for the manager locker.
86 unlockRequests chan unlockRequest
87 lockRequests qu.C
88 holdUnlockRequests chan chan heldUnlock
89 lockState chan bool
90 changePassphrase chan changePassphraseRequest
91 changePassphrases chan changePassphrasesRequest
92 // Information for reorganization handling.
93 // reorganizingLock sync.Mutex
94 // reorganizeToHash chainhash.Hash
95 // reorganizing bool
96 NtfnServer *NotificationServer
97 PodConfig *config.Config
98 chainParams *chaincfg.Params
99 wg sync.WaitGroup
100 started bool
101 quit qu.C
102 quitMu sync.Mutex
103 Update qu.C
104 }
105 106 // Start starts the goroutines necessary to manage a wallet.
107 func (w *Wallet) Start() {
108 T.Ln("starting wallet")
109 w.quitMu.Lock()
110 T.Ln("locked wallet quit mutex")
111 select {
112 case <-w.quit.Wait():
113 T.Ln("waiting for wallet shutdown")
114 // Restart the wallet goroutines after shutdown finishes.
115 w.WaitForShutdown()
116 w.quit = qu.T()
117 default:
118 if w.started {
119 // Ignore when the wallet is still running.
120 I.Ln("wallet already started")
121 w.quitMu.Unlock()
122 return
123 }
124 w.started = true
125 }
126 w.quitMu.Unlock()
127 T.Ln("wallet quit mutex unlocked")
128 w.wg.Add(2)
129 go w.txCreator()
130 go w.walletLocker()
131 }
132 133 // SynchronizeRPC associates the wallet with the consensus RPC client, synchronizes the wallet with the latest changes
134 // to the blockchain, and continuously updates the wallet through RPC notifications.
135 //
136 // This method is unstable and will be removed when all syncing logic is moved outside of the wallet package.
137 func (w *Wallet) SynchronizeRPC(chainClient chainclient.Interface) {
138 T.Ln("SynchronizeRPC")
139 w.quitMu.Lock()
140 select {
141 case <-w.quit.Wait():
142 w.quitMu.Unlock()
143 return
144 default:
145 }
146 w.quitMu.Unlock()
147 // TODO: Ignoring the new client when one is already set breaks callers
148 // who are replacing the client, perhaps after a disconnect.
149 T.Ln("locking wallet chain client mutex")
150 w.chainClientLock.Lock()
151 if w.chainClient != nil {
152 T.Ln("chain client is nil, unlocking wallet chain client mutex")
153 w.chainClientLock.Unlock()
154 return
155 }
156 w.chainClient = chainClient
157 // If the chain client is a NeutrinoClient instance, set a birthday so we don't download all the filters as we go.
158 switch cc := chainClient.(type) {
159 // case *chainclient.NeutrinoClient:
160 // cc.SetStartTime(w.Manager.Birthday())
161 case *chainclient.BitcoindClient:
162 cc.SetBirthday(w.Manager.Birthday())
163 }
164 T.Ln("unlocking wallet chain client mutex")
165 w.chainClientLock.Unlock()
166 T.Ln("unlocked wallet chain client mutex")
167 // TODO: It would be preferable to either run these goroutines separately from the wallet (use wallet mutator
168 // functions to make changes from the RPC client) and not have to stop and restart them each time the client
169 // disconnects and reconnets.
170 w.wg.Add(4)
171 go w.handleChainNotifications()
172 go w.rescanBatchHandler()
173 go w.rescanProgressHandler()
174 go w.rescanRPCHandler()
175 }
176 177 // requireChainClient marks that a wallet method can only be completed when the consensus RPC server is set. This
178 // function and all functions that call it are unstable and will need to be moved when the syncing code is moved out of
179 // the wallet.
180 func (w *Wallet) requireChainClient() (chainclient.Interface, error) {
181 T.Ln("requireChainClient")
182 w.chainClientLock.Lock()
183 chainClient := w.chainClient
184 w.chainClientLock.Unlock()
185 if chainClient == nil {
186 T.Ln("chain client is nil")
187 return nil, errors.New("wallet->chain RPC is inactive")
188 }
189 return chainClient, nil
190 }
191 192 // ChainClient returns the optional consensus RPC client associated with the wallet.
193 //
194 // This function is unstable and will be removed once sync logic is moved out of the wallet.
195 func (w *Wallet) ChainClient() chainclient.Interface {
196 T.Ln("wallet acquiring connect to chain RPC")
197 w.chainClientLock.Lock()
198 T.Ln("chainClientLock locked", w.chainClient == nil)
199 chainClient := w.chainClient
200 w.chainClientLock.Unlock()
201 T.Ln("chainClientLock unlocked")
202 return chainClient
203 }
204 205 // quitChan atomically reads the quit channel.
206 func (w *Wallet) quitChan() qu.C {
207 w.quitMu.Lock()
208 c := w.quit
209 w.quitMu.Unlock()
210 return c
211 }
212 213 // Stop signals all wallet goroutines to shutdown.
214 func (w *Wallet) Stop() {
215 // T.Ln("w", w, "w.quitMu", w.quitMu)
216 w.quitMu.Lock()
217 defer w.quitMu.Unlock()
218 select {
219 case <-w.quit.Wait():
220 default:
221 w.chainClientLock.Lock()
222 if w.chainClient != nil {
223 w.chainClient.Stop()
224 w.chainClient = nil
225 }
226 w.chainClientLock.Unlock()
227 w.quit.Q()
228 // return
229 }
230 }
231 232 // ShuttingDown returns whether the wallet is currently in the process of shutting down or not.
233 func (w *Wallet) ShuttingDown() bool {
234 select {
235 case <-w.quitChan().Wait():
236 return true
237 default:
238 return false
239 }
240 }
241 242 // WaitForShutdown blocks until all wallet goroutines have finished executing.
243 func (w *Wallet) WaitForShutdown() {
244 T.Ln("waiting for shutdown")
245 w.chainClientLock.Lock()
246 T.Ln("locked", w.chainClient)
247 if w.chainClient != nil {
248 T.Ln("calling WaitForShutdown")
249 w.chainClient.WaitForShutdown()
250 }
251 T.Ln("unlocking")
252 w.chainClientLock.Unlock()
253 // T.Ln("waiting on waitgroup")
254 // w.wg.Wait()
255 }
256 257 // SynchronizingToNetwork returns whether the wallet is currently synchronizing with the Bitcoin network.
258 func (w *Wallet) SynchronizingToNetwork() bool {
259 // At the moment, RPC is the only synchronization method. In the future, when SPV is added, a separate check will
260 // also be needed, or SPV could always be enabled if RPC was not explicitly specified when creating the wallet.
261 w.chainClientSyncMtx.Lock()
262 syncing := w.chainClient != nil
263 w.chainClientSyncMtx.Unlock()
264 return syncing
265 }
266 267 // ChainSynced returns whether the wallet has been attached to a chain server and synced up to the best block on the
268 // main chain.
269 func (w *Wallet) ChainSynced() bool {
270 w.chainClientSyncMtx.Lock()
271 synced := w.chainClientSynced
272 w.chainClientSyncMtx.Unlock()
273 return synced
274 }
275 276 // SetChainSynced marks whether the wallet is connected to and currently in sync with the latest block notified by the
277 // chain server.
278 //
279 // NOTE: Due to an API limitation with rpcclient, this may return true after the client disconnected (and is attempting
280 // a reconnect). This will be unknown until the reconnect notification is received, at which point the wallet can be
281 // marked out of sync again until after the next rescan completes.
282 func (w *Wallet) SetChainSynced(synced bool) {
283 w.chainClientSyncMtx.Lock()
284 w.chainClientSynced = synced
285 w.chainClientSyncMtx.Unlock()
286 }
287 288 // activeData returns the currently-active receiving addresses and all unspent outputs. This is primarily intended to
289 // provide the parameters for a rescan request.
290 func (w *Wallet) activeData(dbtx walletdb.ReadTx) (
291 addrs []btcaddr.Address, unspent []wtxmgr.Credit, e error,
292 ) {
293 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
294 txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
295 if e = w.Manager.ForEachActiveAddress(
296 addrmgrNs, func(addr btcaddr.Address) (e error) {
297 addrs = append(addrs, addr)
298 return nil
299 },
300 ); E.Chk(e) {
301 return nil, nil, e
302 }
303 if unspent, e = w.TxStore.UnspentOutputs(txmgrNs); E.Chk(e) {
304 }
305 return addrs, unspent, e
306 }
307 308 // syncWithChain brings the wallet up to date with the current chain server connection. It creates a rescan request and
309 // blocks until the rescan has finished.
310 func (w *Wallet) syncWithChain() (e error) {
311 T.Ln("syncWithChain")
312 var chainClient chainclient.Interface
313 chainClient, e = w.requireChainClient()
314 if e != nil {
315 return e
316 }
317 // Request notifications for transactions sending to all wallet addresses.
318 var (
319 addrs []btcaddr.Address
320 unspent []wtxmgr.Credit
321 )
322 e = walletdb.View(
323 w.db, func(dbtx walletdb.ReadTx) (e error) {
324 addrs, unspent, e = w.activeData(dbtx)
325 return e
326 },
327 )
328 if e != nil {
329 W.Ln("error starting sync", e)
330 return e
331 }
332 startHeight := w.Manager.SyncedTo().Height
333 // We'll mark this as our first sync if we don't have any unspent outputs as known by the wallet. This will allow us
334 // to skip a full rescan at this height, and instead wait for the backend to catch up.
335 isInitialSync := len(unspent) == 0
336 isRecovery := w.recoveryWindow > 0
337 birthday := w.Manager.Birthday()
338 // If an initial sync is attempted, we will try and find the block stamp of the first block past our birthday. This
339 // will be fed into the rescan to ensure we catch transactions that are sent while performing the initial sync.
340 var birthdayStamp *waddrmgr.BlockStamp
341 // TODO(jrick): How should this handle a synced height earlier than the chain server best block? When no addresses
342 // have been generated for the wallet, the rescan can be skipped.
343 //
344 // TODO: This is only correct because activeData above returns all addresses ever created, including those that
345 // don't need to be watched anymore. This code should be updated when this assumption is no longer true, but worst
346 // case would result in an unnecessary rescan.
347 if isInitialSync || isRecovery {
348 // Find the latest checkpoint's height. This lets us catch up to at least that checkpoint, since we're
349 // synchronizing from scratch, and lets us avoid a bunch of costly DB transactions in the case when we're using
350 // BDB for the walletdb backend and Neutrino for the chain.Interface backend, and the chain backend starts
351 // synchronizing at the same time as the wallet.
352 var bestHeight int32
353 _, bestHeight, e = chainClient.GetBestBlock()
354 if e != nil {
355 return e
356 }
357 T.Ln("bestHeight", bestHeight)
358 checkHeight := bestHeight
359 if len(w.chainParams.Checkpoints) > 0 {
360 checkHeight = w.chainParams.Checkpoints[len(
361 w.chainParams.Checkpoints,
362 )-1].Height
363 }
364 logHeight := checkHeight
365 if bestHeight > logHeight {
366 logHeight = bestHeight
367 }
368 I.F(
369 "catching up block hashes to height %d, this will take a while",
370 logHeight,
371 )
372 // Initialize the first database transaction.
373 var tx walletdb.ReadWriteTx
374 tx, e = w.db.BeginReadWriteTx()
375 if e != nil {
376 return e
377 }
378 ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
379 // Only allocate the recoveryMgr if we are actually in recovery mode.
380 recoveryMgr := &RecoveryManager{}
381 if isRecovery {
382 I.Ln(
383 "RECOVERY MODE ENABLED -- rescanning for used addresses with recovery_window =",
384 w.recoveryWindow,
385 )
386 // Initialize the recovery manager with a default batch size of 2000.
387 I.Ln("initialising recovery manager")
388 recoveryMgr = NewRecoveryManager(
389 w.recoveryWindow, recoveryBatchSize,
390 w.chainParams,
391 )
392 // In the event that this recovery is being resumed, we will need to repopulate all found addresses from the
393 // database. For basic recovery, we will only do so for the default scopes.
394 var scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager
395 I.Ln("getting scope managers")
396 scopedMgrs, e = w.defaultScopeManagers()
397 if e != nil {
398 return e
399 }
400 I.Ln("opening read bucket")
401 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
402 var credits []wtxmgr.Credit
403 I.Ln("getting unspent outputs")
404 credits, e = w.TxStore.UnspentOutputs(txmgrNs)
405 if e != nil {
406 return e
407 }
408 I.Ln("resurrecting the dead")
409 e = recoveryMgr.Resurrect(ns, scopedMgrs, credits)
410 if e != nil {
411 return e
412 }
413 I.Ln("all deads now shambling")
414 }
415 I.Ln("startHeight", startHeight, "bestHeight", bestHeight)
416 for height := startHeight; height <= bestHeight; height++ {
417 I.Ln("current height", height)
418 var hash *chainhash.Hash
419 if hash, e = chainClient.GetBlockHash(int64(height)); E.Chk(e) {
420 if e = tx.Rollback(); E.Chk(e) {
421 }
422 return e
423 }
424 // // If we're using the Neutrino backend, we can check if it's current or not. For other backends we'll assume
425 // // it is current if the best height has reached the last checkpoint.
426 // isCurrent := func(bestHeight int32) bool {
427 // switch c := chainClient.(type) {
428 // case *chainclient.NeutrinoClient:
429 // return c.CS.IsCurrent()
430 // }
431 // return bestHeight >= checkHeight
432 // }
433 // If we've found the best height the backend knows about, and the backend is still synchronizing, we'll
434 // wait. We can give it a little bit of time to synchronize further before updating the best height based on
435 // the backend. Once we see that the backend has advanced, we can catch up to it.
436 for height == bestHeight { // && !isCurrent(bestHeight) {
437 I.Ln("getting best height from chain")
438 if _, bestHeight, e = chainClient.GetBestBlock(); E.Chk(e) {
439 if e = tx.Rollback(); E.Chk(e) {
440 }
441 return e
442 } else {
443 break
444 }
445 time.Sleep(time.Second)
446 }
447 var header *wire.BlockHeader
448 header, e = chainClient.GetBlockHeader(hash)
449 if e != nil {
450 return e
451 }
452 // Chk to see if this header's timestamp has surpassed our birthday or if we've surpassed one previously.
453 timestamp := header.Timestamp
454 if timestamp.After(birthday) || birthdayStamp != nil {
455 // If this is the first block past our birthday, record the block stamp so that we can use this as the
456 // starting point for the rescan. This will ensure we don't miss transactions that are sent to the
457 // wallet during an initial sync.
458 //
459 // NOTE: The birthday persisted by the wallet is two days before the actual wallet birthday, to deal
460 // with potentially inaccurate header timestamps.
461 if birthdayStamp == nil {
462 birthdayStamp = &waddrmgr.BlockStamp{
463 Height: height,
464 Hash: *hash,
465 Timestamp: timestamp,
466 }
467 }
468 // If we are in recovery mode and the check passes, we will add this block to our list of blocks to scan
469 // for recovered addresses.
470 if isRecovery {
471 recoveryMgr.AddToBlockBatch(
472 hash, height, timestamp,
473 )
474 }
475 }
476 e = w.Manager.SetSyncedTo(
477 ns, &waddrmgr.BlockStamp{
478 Hash: *hash,
479 Height: height,
480 Timestamp: timestamp,
481 },
482 )
483 if e != nil {
484 e = tx.Rollback()
485 if e != nil {
486 }
487 return e
488 }
489 // If we are in recovery mode, attempt a recovery on blocks that have been added to the recovery manager's
490 // block batch thus far. If block batch is empty, this will be a NOP.
491 if isRecovery && height%recoveryBatchSize == 0 {
492 e = w.recoverDefaultScopes(
493 chainClient, tx, ns,
494 recoveryMgr.BlockBatch(),
495 recoveryMgr.State(),
496 )
497 if e != nil {
498 e = tx.Rollback()
499 if e != nil {
500 }
501 return e
502 }
503 // Clear the batch of all processed blocks.
504 recoveryMgr.ResetBlockBatch()
505 }
506 // Every 10K blocks, commit and start a new database TX.
507 if height%10000 == 0 {
508 e = tx.Commit()
509 if e != nil {
510 e = tx.Rollback()
511 if e != nil {
512 }
513 return e
514 }
515 I.Ln(
516 "caught up to height", height,
517 )
518 tx, e = w.db.BeginReadWriteTx()
519 if e != nil {
520 return e
521 }
522 ns = tx.ReadWriteBucket(waddrmgrNamespaceKey)
523 }
524 }
525 // Perform one last recovery attempt for all blocks that were not batched at the default granularity of 2000
526 // blocks.
527 if isRecovery {
528 I.Ln("isRecovery")
529 e = w.recoverDefaultScopes(
530 chainClient, tx, ns, recoveryMgr.BlockBatch(),
531 recoveryMgr.State(),
532 )
533 if e != nil {
534 e = tx.Rollback()
535 if e != nil {
536 }
537 return e
538 }
539 }
540 // Commit (or roll back) the final database transaction.
541 if e = tx.Commit(); E.Chk(e) {
542 if e = tx.Rollback(); E.Chk(e) {
543 }
544 return e
545 }
546 I.Ln("done catching up block hashes")
547 // Since we've spent some time catching up block hashes, we might have new addresses waiting for us that were
548 // requested during initial sync. Make sure we have those before we request a rescan later on.
549 e = walletdb.View(
550 w.db, func(dbtx walletdb.ReadTx) (e error) {
551 addrs, unspent, e = w.activeData(dbtx)
552 return e
553 },
554 )
555 if e != nil {
556 return e
557 }
558 }
559 // Compare previously-seen blocks against the chain server. If any of these blocks no longer exist, rollback all of
560 // the missing blocks before catching up with the rescan.
561 rollback := false
562 rollbackStamp := w.Manager.SyncedTo()
563 e = walletdb.Update(
564 w.db, func(tx walletdb.ReadWriteTx) (e error) {
565 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
566 txmgrNs := tx.ReadWriteBucket(wtxmgrNamespaceKey)
567 for height := rollbackStamp.Height; true; height-- {
568 hash, e := w.Manager.BlockHash(addrmgrNs, height)
569 if e != nil {
570 return e
571 }
572 chainHash, e := chainClient.GetBlockHash(int64(height))
573 if e != nil {
574 return e
575 }
576 header, e := chainClient.GetBlockHeader(chainHash)
577 if e != nil {
578 return e
579 }
580 rollbackStamp.Hash = *chainHash
581 rollbackStamp.Height = height
582 rollbackStamp.Timestamp = header.Timestamp
583 if bytes.Equal(hash[:], chainHash[:]) {
584 break
585 }
586 rollback = true
587 }
588 if rollback {
589 e := w.Manager.SetSyncedTo(addrmgrNs, &rollbackStamp)
590 if e != nil {
591 return e
592 }
593 // Rollback unconfirms transactions at and beyond the passed height, so add one to the new synced-to height
594 // to prevent unconfirming txs from the synced-to block.
595 e = w.TxStore.Rollback(txmgrNs, rollbackStamp.Height+1)
596 if e != nil {
597 return e
598 }
599 }
600 return nil
601 },
602 )
603 if e != nil {
604 return e
605 }
606 // If a birthday stamp was found during the initial sync and the rollback causes us to revert it, update the
607 // birthday stamp so that it points at the new tip.
608 if birthdayStamp != nil && rollbackStamp.Height <= birthdayStamp.Height {
609 birthdayStamp = &rollbackStamp
610 }
611 // Request notifications for connected and disconnected blocks.
612 //
613 // TODO(jrick): Either request this notification only once, or when rpcclient is modified to allow some notification
614 // request to not automatically resent on reconnect, include the notifyblocks request as well. I am leaning towards
615 // allowing off all rpcclient notification re-registrations, in which case the code here should be left as is.
616 e = chainClient.NotifyBlocks()
617 if e != nil {
618 return e
619 }
620 return w.rescanWithTarget(addrs, unspent, birthdayStamp)
621 }
622 623 // defaultScopeManagers fetches the ScopedKeyManagers from the wallet using the default set of key scopes.
624 func (w *Wallet) defaultScopeManagers() (
625 map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager, error,
626 ) {
627 scopedMgrs := make(map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager)
628 for _, scope := range waddrmgr.DefaultKeyScopes {
629 scopedMgr, e := w.Manager.FetchScopedKeyManager(scope)
630 if e != nil {
631 return nil, e
632 }
633 scopedMgrs[scope] = scopedMgr
634 }
635 return scopedMgrs, nil
636 }
637 638 // recoverDefaultScopes attempts to recover any addresses belonging to any active scoped key managers known to the
639 // wallet. Recovery of each scope's default account will be done iteratively against the same batch of blocks.
640 //
641 // TODO(conner): parallelize/pipeline/cache intermediate network requests
642 func (w *Wallet) recoverDefaultScopes(
643 chainClient chainclient.Interface,
644 tx walletdb.ReadWriteTx,
645 ns walletdb.ReadWriteBucket,
646 batch []wtxmgr.BlockMeta,
647 recoveryState *RecoveryState,
648 ) (e error) {
649 scopedMgrs, e := w.defaultScopeManagers()
650 if e != nil {
651 return e
652 }
653 return w.recoverScopedAddresses(
654 chainClient, tx, ns, batch, recoveryState, scopedMgrs,
655 )
656 }
657 658 // recoverAccountAddresses scans a range of blocks in attempts to recover any previously used addresses for a particular
659 // account derivation path. At a high level, the algorithm works as follows:
660 //
661 // 1) Ensure internal and external branch horizons are fully expanded.
662 //
663 // 2) Filter the entire range of blocks, stopping if a non-zero number of address are contained in a particular block.
664 //
665 // 3) Record all internal and external addresses found in the block.
666 //
667 // 4) Record any outpoints found in the block that should be watched for spends
668 //
669 // 5) Trim the range of blocks up to and including the one reporting the addrs.
670 //
671 // 6) Repeat from (1) if there are still more blocks in the range.
672 func (w *Wallet) recoverScopedAddresses(
673 chainClient chainclient.Interface,
674 tx walletdb.ReadWriteTx,
675 ns walletdb.ReadWriteBucket,
676 batch []wtxmgr.BlockMeta,
677 recoveryState *RecoveryState,
678 scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager,
679 ) (e error) {
680 // If there are no blocks in the batch, we are done.
681 if len(batch) == 0 {
682 return nil
683 }
684 I.F(
685 "scanning %d blocks for recoverable addresses",
686 len(batch),
687 )
688 expandHorizons:
689 for scope, scopedMgr := range scopedMgrs {
690 scopeState := recoveryState.StateForScope(scope)
691 e = expandScopeHorizons(ns, scopedMgr, scopeState)
692 if e != nil {
693 return e
694 }
695 }
696 // With the internal and external horizons properly expanded, we now construct the filter blocks request. The
697 // request includes the range of blocks we intend to scan, in addition to the scope-index -> addr map for all
698 // internal and external branches.
699 filterReq := newFilterBlocksRequest(batch, scopedMgrs, recoveryState)
700 // Initiate the filter blocks request using our chain backend. If an error occurs, we are unable to proceed with the
701 // recovery.
702 filterResp, e := chainClient.FilterBlocks(filterReq)
703 if e != nil {
704 return e
705 }
706 // If the filter response is empty, this signals that the rest of the batch was completed, and no other addresses
707 // were discovered. As a result, no further modifications to our recovery state are required and we can proceed to
708 // the next batch.
709 if filterResp == nil {
710 return nil
711 }
712 // Otherwise, retrieve the block info for the block that detected a non-zero number of address matches.
713 block := batch[filterResp.BatchIndex]
714 // Log any non-trivial findings of addresses or outpoints.
715 logFilterBlocksResp(block, filterResp)
716 // Report any external or internal addresses found as a result of the appropriate branch recovery state. Adding
717 // indexes above the last-found index of either will result in the horizons being expanded upon the next iteration.
718 // Any found addresses are also marked used using the scoped key manager.
719 e = extendFoundAddresses(ns, filterResp, scopedMgrs, recoveryState)
720 if e != nil {
721 return e
722 }
723 // Update the global set of watched outpoints with any that were found in the block.
724 for outPoint, addr := range filterResp.FoundOutPoints {
725 recoveryState.AddWatchedOutPoint(&outPoint, addr)
726 }
727 // Finally, record all of the relevant transactions that were returned in the filter blocks response. This ensures
728 // that these transactions and their outputs are tracked when the final rescan is performed.
729 for _, txn := range filterResp.RelevantTxns {
730 txRecord, e := wtxmgr.NewTxRecordFromMsgTx(
731 txn, filterResp.BlockMeta.Time,
732 )
733 if e != nil {
734 return e
735 }
736 e = w.addRelevantTx(tx, txRecord, &filterResp.BlockMeta)
737 if e != nil {
738 return e
739 }
740 }
741 // Update the batch to indicate that we've processed all block through the one that returned found addresses.
742 batch = batch[filterResp.BatchIndex+1:]
743 // If this was not the last block in the batch, we will repeat the filtering process again after expanding our
744 // horizons.
745 if len(batch) > 0 {
746 goto expandHorizons
747 }
748 return nil
749 }
750 751 // expandScopeHorizons ensures that the ScopeRecoveryState has an adequately sized look ahead for both its internal and
752 // external branches. The keys derived here are added to the scope's recovery state, but do not affect the persistent
753 // state of the wallet. If any invalid child keys are detected, the horizon will be properly extended such that our
754 // lookahead always includes the proper number of valid child keys.
755 func expandScopeHorizons(
756 ns walletdb.ReadWriteBucket,
757 scopedMgr *waddrmgr.ScopedKeyManager,
758 scopeState *ScopeRecoveryState,
759 ) (e error) {
760 // Compute the current external horizon and the number of addresses we must derive to ensure we maintain a
761 // sufficient recovery window for the external branch.
762 exHorizon, exWindow := scopeState.ExternalBranch.ExtendHorizon()
763 count, childIndex := uint32(0), exHorizon
764 for count < exWindow {
765 keyPath := externalKeyPath(childIndex)
766 var ep error
767 var addr waddrmgr.ManagedAddress
768 addr, ep = scopedMgr.DeriveFromKeyPath(ns, keyPath)
769 switch {
770 case ep == hdkeychain.ErrInvalidChild:
771 // Record the existence of an invalid child with the external branch's recovery state. This also increments
772 // the branch's horizon so that it accounts for this skipped child index.
773 scopeState.ExternalBranch.MarkInvalidChild(childIndex)
774 childIndex++
775 continue
776 case ep != nil:
777 return ep
778 }
779 // Register the newly generated external address and child index with the external branch recovery state.
780 scopeState.ExternalBranch.AddAddr(childIndex, addr.Address())
781 childIndex++
782 count++
783 }
784 // Compute the current internal horizon and the number of addresses we must derive to ensure we maintain a
785 // sufficient recovery window for the internal branch.
786 inHorizon, inWindow := scopeState.InternalBranch.ExtendHorizon()
787 count, childIndex = 0, inHorizon
788 for count < inWindow {
789 keyPath := internalKeyPath(childIndex)
790 addr, e := scopedMgr.DeriveFromKeyPath(ns, keyPath)
791 switch {
792 case e == hdkeychain.ErrInvalidChild:
793 // Record the existence of an invalid child with the internal branch's recovery state. This also increments
794 // the branch's horizon so that it accounts for this skipped child index.
795 scopeState.InternalBranch.MarkInvalidChild(childIndex)
796 childIndex++
797 continue
798 case e != nil:
799 return e
800 }
801 // Register the newly generated internal address and child index with the internal branch recovery state.
802 scopeState.InternalBranch.AddAddr(childIndex, addr.Address())
803 childIndex++
804 count++
805 }
806 return nil
807 }
808 809 // externalKeyPath returns the relative external derivation path /0/0/index.
810 func externalKeyPath(index uint32) waddrmgr.DerivationPath {
811 return waddrmgr.DerivationPath{
812 Account: waddrmgr.DefaultAccountNum,
813 Branch: waddrmgr.ExternalBranch,
814 Index: index,
815 }
816 }
817 818 // internalKeyPath returns the relative internal derivation path /0/1/index.
819 func internalKeyPath(index uint32) waddrmgr.DerivationPath {
820 return waddrmgr.DerivationPath{
821 Account: waddrmgr.DefaultAccountNum,
822 Branch: waddrmgr.InternalBranch,
823 Index: index,
824 }
825 }
826 827 // newFilterBlocksRequest constructs FilterBlocksRequests using our current block range, scoped managers, and recovery
828 // state.
829 func newFilterBlocksRequest(
830 batch []wtxmgr.BlockMeta,
831 scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager,
832 recoveryState *RecoveryState,
833 ) *chainclient.FilterBlocksRequest {
834 filterReq := &chainclient.FilterBlocksRequest{
835 Blocks: batch,
836 ExternalAddrs: make(map[waddrmgr.ScopedIndex]btcaddr.Address),
837 InternalAddrs: make(map[waddrmgr.ScopedIndex]btcaddr.Address),
838 WatchedOutPoints: recoveryState.WatchedOutPoints(),
839 }
840 // Populate the external and internal addresses by merging the addresses sets belong to all currently tracked
841 // scopes.
842 for scope := range scopedMgrs {
843 scopeState := recoveryState.StateForScope(scope)
844 for index, addr := range scopeState.ExternalBranch.Addrs() {
845 scopedIndex := waddrmgr.ScopedIndex{
846 Scope: scope,
847 Index: index,
848 }
849 filterReq.ExternalAddrs[scopedIndex] = addr
850 }
851 for index, addr := range scopeState.InternalBranch.Addrs() {
852 scopedIndex := waddrmgr.ScopedIndex{
853 Scope: scope,
854 Index: index,
855 }
856 filterReq.InternalAddrs[scopedIndex] = addr
857 }
858 }
859 return filterReq
860 }
861 862 // extendFoundAddresses accepts a filter blocks response that contains addresses found on chain, and advances the state
863 // of all relevant derivation paths to match the highest found child index for each branch.
864 func extendFoundAddresses(
865 ns walletdb.ReadWriteBucket,
866 filterResp *chainclient.FilterBlocksResponse,
867 scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager,
868 recoveryState *RecoveryState,
869 ) (e error) {
870 // Mark all recovered external addresses as used. This will be done only for scopes that reported a non-zero number
871 // of external addresses in this block.
872 for scope, indexes := range filterResp.FoundExternalAddrs {
873 // First, report all external child indexes found for this scope. This ensures that the external last-found
874 // index will be updated to include the maximum child index seen thus far.
875 scopeState := recoveryState.StateForScope(scope)
876 for index := range indexes {
877 scopeState.ExternalBranch.ReportFound(index)
878 }
879 scopedMgr := scopedMgrs[scope]
880 // Now, with all found addresses reported, derive and extend all external addresses up to and including the
881 // current last found index for this scope.
882 exNextUnfound := scopeState.ExternalBranch.NextUnfound()
883 exLastFound := exNextUnfound
884 if exLastFound > 0 {
885 exLastFound--
886 }
887 e := scopedMgr.ExtendExternalAddresses(
888 ns, waddrmgr.DefaultAccountNum, exLastFound,
889 )
890 if e != nil {
891 return e
892 }
893 // Finally, with the scope's addresses extended, we mark used the external addresses that were found in the
894 // block and belong to this scope.
895 for index := range indexes {
896 addr := scopeState.ExternalBranch.GetAddr(index)
897 e := scopedMgr.MarkUsed(ns, addr)
898 if e != nil {
899 return e
900 }
901 }
902 }
903 // Mark all recovered internal addresses as used. This will be done only for scopes that reported a non-zero number
904 // of internal addresses in this block.
905 for scope, indexes := range filterResp.FoundInternalAddrs {
906 // First, report all internal child indexes found for this scope. This ensures that the internal last-found
907 // index will be updated to include the maximum child index seen thus far.
908 scopeState := recoveryState.StateForScope(scope)
909 for index := range indexes {
910 scopeState.InternalBranch.ReportFound(index)
911 }
912 scopedMgr := scopedMgrs[scope]
913 // Now, with all found addresses reported, derive and extend all internal addresses up to and including the
914 // current last found index for this scope.
915 inNextUnfound := scopeState.InternalBranch.NextUnfound()
916 inLastFound := inNextUnfound
917 if inLastFound > 0 {
918 inLastFound--
919 }
920 e := scopedMgr.ExtendInternalAddresses(
921 ns, waddrmgr.DefaultAccountNum, inLastFound,
922 )
923 if e != nil {
924 return e
925 }
926 // Finally, with the scope's addresses extended, we mark used the internal addresses that were found in the
927 // block and belong to this scope.
928 for index := range indexes {
929 addr := scopeState.InternalBranch.GetAddr(index)
930 e := scopedMgr.MarkUsed(ns, addr)
931 if e != nil {
932 return e
933 }
934 }
935 }
936 return nil
937 }
938 939 // logFilterBlocksResp provides useful logging information when filtering succeeded in finding relevant transactions.
940 func logFilterBlocksResp(
941 block wtxmgr.BlockMeta,
942 resp *chainclient.FilterBlocksResponse,
943 ) {
944 // Log the number of external addresses found in this block.
945 var nFoundExternal int
946 for _, indexes := range resp.FoundExternalAddrs {
947 nFoundExternal += len(indexes)
948 }
949 if nFoundExternal > 0 {
950 T.F(
951 "recovered %d external addrs at height=%d hash=%v",
952 nFoundExternal, block.Height, block.Hash,
953 )
954 }
955 // Log the number of internal addresses found in this block.
956 var nFoundInternal int
957 for _, indexes := range resp.FoundInternalAddrs {
958 nFoundInternal += len(indexes)
959 }
960 if nFoundInternal > 0 {
961 T.F(
962 "recovered %d internal addrs at height=%d hash=%v",
963 nFoundInternal, block.Height, block.Hash,
964 )
965 }
966 // Log the number of outpoints found in this block.
967 nFoundOutPoints := len(resp.FoundOutPoints)
968 if nFoundOutPoints > 0 {
969 T.F(
970 "found %d spends from watched outpoints at height=%d hash=%v",
971 nFoundOutPoints, block.Height, block.Hash,
972 )
973 }
974 }
975 976 type (
977 createTxRequest struct {
978 account uint32
979 outputs []*wire.TxOut
980 minconf int32
981 feeSatPerKB amt.Amount
982 resp chan createTxResponse
983 }
984 createTxResponse struct {
985 tx *txauthor.AuthoredTx
986 e error
987 }
988 )
989 990 // txCreator is responsible for the input selection and creation of transactions. These functions are the responsibility
991 // of this method (designed to be run as its own goroutine) since input selection must be serialized, or else it is
992 // possible to create double spends by choosing the same inputs for multiple transactions. Along with input selection,
993 // this method is also responsible for the signing of transactions, since we don't want to end up in a situation where
994 // we run out of inputs as multiple transactions are being created. In this situation, it would then be possible for
995 // both requests, rather than just one, to fail due to not enough available inputs.
996 func (w *Wallet) txCreator() {
997 quit := w.quitChan()
998 out:
999 for {
1000 select {
1001 case txr := <-w.createTxRequests:
1002 var e error
1003 var h heldUnlock
1004 h, e = w.holdUnlock()
1005 if e != nil {
1006 txr.resp <- createTxResponse{nil, e}
1007 continue
1008 }
1009 var tx *txauthor.AuthoredTx
1010 tx, e = w.txToOutputs(
1011 txr.outputs, txr.account,
1012 txr.minconf, txr.feeSatPerKB,
1013 )
1014 h.release()
1015 txr.resp <- createTxResponse{tx, e}
1016 case <-quit.Wait():
1017 break out
1018 }
1019 }
1020 w.wg.Done()
1021 }
1022 1023 // CreateSimpleTx creates a new signed transaction spending unspent P2PKH outputs with at least minconf confirmations
1024 // spending to any number of address/amount pairs. Change and an appropriate transaction fee are automatically included,
1025 // if necessary. All transaction creation through this function is serialized to prevent the creation of many
1026 // transactions which spend the same outputs.
1027 func (w *Wallet) CreateSimpleTx(
1028 account uint32, outputs []*wire.TxOut,
1029 minconf int32, satPerKb amt.Amount,
1030 ) (*txauthor.AuthoredTx, error) {
1031 req := createTxRequest{
1032 account: account,
1033 outputs: outputs,
1034 minconf: minconf,
1035 feeSatPerKB: satPerKb,
1036 resp: make(chan createTxResponse),
1037 }
1038 w.createTxRequests <- req
1039 resp := <-req.resp
1040 return resp.tx, resp.e
1041 }
1042 1043 type (
1044 unlockRequest struct {
1045 passphrase []byte
1046 lockAfter <-chan time.Time // nil prevents the timeout.
1047 err chan error
1048 }
1049 changePassphraseRequest struct {
1050 old, new []byte
1051 private bool
1052 err chan error
1053 }
1054 changePassphrasesRequest struct {
1055 publicOld, publicNew []byte
1056 privateOld, privateNew []byte
1057 err chan error
1058 }
1059 // heldUnlock is a tool to prevent the wallet from automatically locking after some timeout before an operation
1060 // which needed the unlocked wallet has finished. Any acquired heldUnlock *must* be released (preferably with a
1061 // defer) or the wallet will forever remain unlocked.
1062 heldUnlock qu.C
1063 )
1064 1065 // walletLocker manages the locked/unlocked state of a wallet.
1066 func (w *Wallet) walletLocker() {
1067 var timeout <-chan time.Time
1068 holdChan := make(heldUnlock)
1069 quit := w.quitChan()
1070 // this flips to false once the first unlock has been done, for runasservice opt which shuts down on lock
1071 // first := true
1072 var e error
1073 out:
1074 for {
1075 select {
1076 case req := <-w.unlockRequests:
1077 e = walletdb.View(
1078 w.db, func(tx walletdb.ReadTx) (e error) {
1079 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1080 return w.Manager.Unlock(addrmgrNs, req.passphrase)
1081 },
1082 )
1083 if e != nil {
1084 req.err <- e
1085 continue
1086 }
1087 timeout = req.lockAfter
1088 if timeout == nil {
1089 I.Ln("the wallet has been unlocked without a time limit")
1090 } else {
1091 I.Ln("the wallet has been temporarily unlocked")
1092 }
1093 req.err <- nil
1094 continue
1095 case req := <-w.changePassphrase:
1096 e = walletdb.Update(
1097 w.db, func(tx walletdb.ReadWriteTx) (e error) {
1098 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
1099 return w.Manager.ChangePassphrase(
1100 addrmgrNs, req.old, req.new, req.private,
1101 &waddrmgr.DefaultScryptOptions,
1102 )
1103 },
1104 )
1105 req.err <- e
1106 continue
1107 case req := <-w.changePassphrases:
1108 e = walletdb.Update(
1109 w.db, func(tx walletdb.ReadWriteTx) (e error) {
1110 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
1111 e = w.Manager.ChangePassphrase(
1112 addrmgrNs, req.publicOld, req.publicNew,
1113 false, &waddrmgr.DefaultScryptOptions,
1114 )
1115 if e != nil {
1116 return e
1117 }
1118 return w.Manager.ChangePassphrase(
1119 addrmgrNs, req.privateOld, req.privateNew,
1120 true, &waddrmgr.DefaultScryptOptions,
1121 )
1122 },
1123 )
1124 req.err <- e
1125 continue
1126 case req := <-w.holdUnlockRequests:
1127 if w.Manager.IsLocked() {
1128 close(req)
1129 continue
1130 }
1131 req <- holdChan
1132 <-holdChan // Block until the lock is released.
1133 // If, after holding onto the unlocked wallet for some time, the timeout has expired, lock it now instead of
1134 // hoping it gets unlocked next time the top level select runs.
1135 select {
1136 case <-timeout:
1137 // Let the top level select fallthrough so the wallet is locked.
1138 default:
1139 continue
1140 }
1141 case w.lockState <- w.Manager.IsLocked():
1142 continue
1143 case <-quit.Wait():
1144 break out
1145 case <-w.lockRequests.Wait():
1146 // first = false
1147 case <-timeout:
1148 // first = false
1149 }
1150 // Select statement fell through by an explicit lock or the timer expiring. Lock the manager here.
1151 timeout = nil
1152 e = w.Manager.Lock()
1153 if e != nil && !waddrmgr.IsError(e, waddrmgr.ErrLocked) {
1154 E.Ln("could not lock wallet:", e)
1155 } else {
1156 I.Ln("the wallet has been locked")
1157 }
1158 // if *w.PodConfig.RunAsService && !first {
1159 // // if we are running as a service this means shut down on lock as unlocking happens only at startup
1160 // break out
1161 // }
1162 }
1163 w.wg.Done()
1164 }
1165 1166 // Unlock unlocks the wallet's address manager and relocks it after timeout has expired. If the wallet is already
1167 // unlocked and the new passphrase is correct, the current timeout is replaced with the new one. The wallet will be
1168 // locked if the passphrase is incorrect or any other error occurs during the unlock.
1169 func (w *Wallet) Unlock(passphrase []byte, lock <-chan time.Time) (e error) {
1170 eC := make(chan error, 1)
1171 w.unlockRequests <- unlockRequest{
1172 passphrase: passphrase,
1173 lockAfter: lock,
1174 err: eC,
1175 }
1176 return <-eC
1177 }
1178 1179 // Lock locks the wallet's address manager.
1180 func (w *Wallet) Lock() {
1181 w.lockRequests <- struct{}{}
1182 }
1183 1184 // Locked returns whether the account manager for a wallet is locked.
1185 func (w *Wallet) Locked() bool {
1186 return <-w.lockState
1187 }
1188 1189 // holdUnlock prevents the wallet from being locked. The heldUnlock object *must* be released, or the wallet will
1190 // forever remain unlocked.
1191 //
1192 // TODO: To prevent the above scenario, perhaps closures should be passed to the walletLocker goroutine and disallow
1193 // callers from explicitly handling the locking mechanism.
1194 func (w *Wallet) holdUnlock() (heldUnlock, error) {
1195 req := make(chan heldUnlock)
1196 w.holdUnlockRequests <- req
1197 hl, ok := <-req
1198 if !ok {
1199 // TODO(davec): This should be defined and exported from waddrmgr.
1200 return nil, waddrmgr.ManagerError{
1201 ErrorCode: waddrmgr.ErrLocked,
1202 Description: "address manager is locked",
1203 }
1204 }
1205 return hl, nil
1206 }
1207 1208 // release releases the hold on the unlocked-state of the wallet and allows the wallet to be locked again. If a lock
1209 // timeout has already expired, the wallet is locked again as soon as release is called.
1210 func (c heldUnlock) release() {
1211 c <- struct{}{}
1212 }
1213 1214 // ChangePrivatePassphrase attempts to change the passphrase for a wallet from old to new. Changing the passphrase is
1215 // synchronized with all other address manager locking and unlocking. The lock state will be the same as it was before
1216 // the password change.
1217 func (w *Wallet) ChangePrivatePassphrase(old, new []byte) (e error) {
1218 errChan := make(chan error, 1)
1219 w.changePassphrase <- changePassphraseRequest{
1220 old: old,
1221 new: new,
1222 private: true,
1223 err: errChan,
1224 }
1225 return <-errChan
1226 }
1227 1228 // ChangePublicPassphrase modifies the public passphrase of the wallet.
1229 func (w *Wallet) ChangePublicPassphrase(old, new []byte) (e error) {
1230 errChan := make(chan error, 1)
1231 w.changePassphrase <- changePassphraseRequest{
1232 old: old,
1233 new: new,
1234 private: false,
1235 err: errChan,
1236 }
1237 return <-errChan
1238 }
1239 1240 // ChangePassphrases modifies the public and private passphrase of the wallet atomically.
1241 func (w *Wallet) ChangePassphrases(
1242 publicOld, publicNew, privateOld,
1243 privateNew []byte,
1244 ) (e error) {
1245 errChan := make(chan error, 1)
1246 w.changePassphrases <- changePassphrasesRequest{
1247 publicOld: publicOld,
1248 publicNew: publicNew,
1249 privateOld: privateOld,
1250 privateNew: privateNew,
1251 err: errChan,
1252 }
1253 return <-errChan
1254 }
1255 1256 // // accountUsed returns whether there are any recorded transactions spending to
1257 // // a given account. It returns true if atleast one address in the account was
1258 // // used and false if no address in the account was used.
1259 // func (w *Wallet) accountUsed(addrmgrNs walletdb.ReadWriteBucket, account uint32) (bool, error) {
1260 // var used bool
1261 // e := w.Manager.ForEachAccountAddress(addrmgrNs, account,
1262 // func(maddr waddrmgr.ManagedAddress) (e error) {
1263 // used = maddr.Used(addrmgrNs)
1264 // if used {
1265 // return waddrmgr.Break
1266 // }
1267 // return nil
1268 // })
1269 // if e == waddrmgr.Break {
1270 // e = nil
1271 // }
1272 // return used, err
1273 // }
1274 1275 // AccountAddresses returns the addresses for every created address for an
1276 // account.
1277 func (w *Wallet) AccountAddresses(account uint32) (
1278 addrs []btcaddr.Address, e error,
1279 ) {
1280 e = walletdb.View(
1281 w.db, func(tx walletdb.ReadTx) (e error) {
1282 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1283 return w.Manager.ForEachAccountAddress(
1284 addrmgrNs, account, func(maddr waddrmgr.ManagedAddress) (e error) {
1285 addrs = append(addrs, maddr.Address())
1286 return nil
1287 },
1288 )
1289 },
1290 )
1291 return
1292 }
1293 1294 // CalculateBalance sums the amounts of all unspent transaction outputs to addresses of a wallet and returns the
1295 // balance.
1296 //
1297 // If confirmations is 0, all UTXOs, even those not present in a block (height -1), will be used to get the balance.
1298 // Otherwise, a UTXO must be in a block. If confirmations is 1 or greater, the balance will be calculated based on how
1299 // many how many blocks include a UTXO.
1300 func (w *Wallet) CalculateBalance(confirms int32) (
1301 balance amt.Amount, e error,
1302 ) {
1303 e = walletdb.View(
1304 w.db, func(tx walletdb.ReadTx) (e error) {
1305 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1306 blk := w.Manager.SyncedTo()
1307 balance, e = w.TxStore.Balance(txmgrNs, confirms, blk.Height)
1308 return e
1309 },
1310 )
1311 return balance, e
1312 }
1313 1314 // Balances records total, spendable (by policy), and immature coinbase reward balance amounts.
1315 type Balances struct {
1316 Total amt.Amount
1317 Spendable amt.Amount
1318 ImmatureReward amt.Amount
1319 }
1320 1321 // CalculateAccountBalances sums the amounts of all unspent transaction outputs to the given account of a wallet and
1322 // returns the balance.
1323 //
1324 // This function is much slower than it needs to be since transactions outputs are not indexed by the accounts they
1325 // credit to, and all unspent transaction outputs must be iterated.
1326 func (w *Wallet) CalculateAccountBalances(
1327 account uint32, confirms int32,
1328 ) (bals Balances, e error) {
1329 e = walletdb.View(
1330 w.db, func(tx walletdb.ReadTx) (e error) {
1331 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1332 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1333 // Get current block. The block height used for calculating
1334 // the number of tx confirmations.
1335 syncBlock := w.Manager.SyncedTo()
1336 var unspent []wtxmgr.Credit
1337 unspent, e = w.TxStore.UnspentOutputs(txmgrNs)
1338 if e != nil {
1339 return e
1340 }
1341 for i := range unspent {
1342 output := &unspent[i]
1343 var outputAcct uint32
1344 var addrs []btcaddr.Address
1345 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
1346 output.PkScript, w.chainParams,
1347 )
1348 if e == nil && len(addrs) > 0 {
1349 _, outputAcct, e = w.Manager.AddrAccount(addrmgrNs, addrs[0])
1350 }
1351 if e != nil || outputAcct != account {
1352 continue
1353 }
1354 bals.Total += output.Amount
1355 if output.FromCoinBase && !confirmed(
1356 int32(w.chainParams.CoinbaseMaturity),
1357 output.Height, syncBlock.Height,
1358 ) {
1359 bals.ImmatureReward += output.Amount
1360 } else if confirmed(confirms, output.Height, syncBlock.Height) {
1361 bals.Spendable += output.Amount
1362 }
1363 }
1364 return nil
1365 },
1366 )
1367 return bals, e
1368 }
1369 1370 // CurrentAddress gets the most recently requested Bitcoin payment address from a wallet for a particular key-chain
1371 // scope. If the address has already been used (there is at least one transaction spending to it in the blockchain or
1372 // pod mempool), the next chained address is returned.
1373 func (w *Wallet) CurrentAddress(
1374 account uint32, scope waddrmgr.KeyScope,
1375 ) (btcaddr.Address, error) {
1376 chainClient, e := w.requireChainClient()
1377 if e != nil {
1378 return nil, e
1379 }
1380 manager, e := w.Manager.FetchScopedKeyManager(scope)
1381 if e != nil {
1382 return nil, e
1383 }
1384 var (
1385 addr btcaddr.Address
1386 props *waddrmgr.AccountProperties
1387 )
1388 e = walletdb.Update(
1389 w.db, func(tx walletdb.ReadWriteTx) (e error) {
1390 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
1391 maddr, e := manager.LastExternalAddress(addrmgrNs, account)
1392 if e != nil {
1393 // If no address exists yet, create the first external address.
1394 if waddrmgr.IsError(e, waddrmgr.ErrAddressNotFound) {
1395 addr, props, e = w.newAddress(
1396 addrmgrNs, account, scope,
1397 )
1398 }
1399 return e
1400 }
1401 // Get next chained address if the last one has already been used.
1402 if maddr.Used(addrmgrNs) {
1403 addr, props, e = w.newAddress(
1404 addrmgrNs, account, scope,
1405 )
1406 return e
1407 }
1408 addr = maddr.Address()
1409 return nil
1410 },
1411 )
1412 if e != nil {
1413 return nil, e
1414 }
1415 // If the props have been initially, then we had to create a new address to satisfy the query. Notify the rpc server
1416 // about the new address.
1417 if props != nil {
1418 e = chainClient.NotifyReceived([]btcaddr.Address{addr})
1419 if e != nil {
1420 return nil, e
1421 }
1422 w.NtfnServer.notifyAccountProperties(props)
1423 }
1424 return addr, nil
1425 }
1426 1427 // PubKeyForAddress looks up the associated public key for a P2PKH address.
1428 func (w *Wallet) PubKeyForAddress(a btcaddr.Address) (
1429 pubKey *ec.PublicKey, e error,
1430 ) {
1431 e = walletdb.View(
1432 w.db, func(tx walletdb.ReadTx) (e error) {
1433 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1434 managedAddr, e := w.Manager.Address(addrmgrNs, a)
1435 if e != nil {
1436 return e
1437 }
1438 managedPubKeyAddr, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
1439 if !ok {
1440 return errors.New("address does not have an associated public key")
1441 }
1442 pubKey = managedPubKeyAddr.PubKey()
1443 return nil
1444 },
1445 )
1446 return
1447 }
1448 1449 // PrivKeyForAddress looks up the associated private key for a P2PKH or P2PK address.
1450 func (w *Wallet) PrivKeyForAddress(a btcaddr.Address) (
1451 privKey *ec.PrivateKey, e error,
1452 ) {
1453 e = walletdb.View(
1454 w.db, func(tx walletdb.ReadTx) (e error) {
1455 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1456 managedAddr, e := w.Manager.Address(addrmgrNs, a)
1457 if e != nil {
1458 return e
1459 }
1460 managedPubKeyAddr, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
1461 if !ok {
1462 return errors.New("address does not have an associated private key")
1463 }
1464 privKey, e = managedPubKeyAddr.PrivKey()
1465 return e
1466 },
1467 )
1468 return privKey, e
1469 }
1470 1471 // HaveAddress returns whether the wallet is the owner of the address a.
1472 func (w *Wallet) HaveAddress(a btcaddr.Address) (b bool, e error) {
1473 e = walletdb.View(
1474 w.db, func(tx walletdb.ReadTx) (e error) {
1475 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1476 _, e = w.Manager.Address(addrmgrNs, a)
1477 return e
1478 },
1479 )
1480 if e == nil {
1481 return true, nil
1482 }
1483 if waddrmgr.IsError(e, waddrmgr.ErrAddressNotFound) {
1484 return false, nil
1485 }
1486 return false, e
1487 }
1488 1489 // AccountOfAddress finds the account that an address is associated with.
1490 func (w *Wallet) AccountOfAddress(a btcaddr.Address) (account uint32, e error) {
1491 e = walletdb.View(
1492 w.db, func(tx walletdb.ReadTx) (e error) {
1493 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1494 _, account, e = w.Manager.AddrAccount(addrmgrNs, a)
1495 return e
1496 },
1497 )
1498 return account, e
1499 }
1500 1501 // AddressInfo returns detailed information regarding a wallet address.
1502 func (w *Wallet) AddressInfo(a btcaddr.Address) (
1503 waddrmgr.ManagedAddress, error,
1504 ) {
1505 var managedAddress waddrmgr.ManagedAddress
1506 e := walletdb.View(
1507 w.db, func(tx walletdb.ReadTx) (e error) {
1508 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1509 managedAddress, e = w.Manager.Address(addrmgrNs, a)
1510 return e
1511 },
1512 )
1513 return managedAddress, e
1514 }
1515 1516 // AccountNumber returns the account number for an account name under a particular key scope.
1517 func (w *Wallet) AccountNumber(
1518 scope waddrmgr.KeyScope, accountName string,
1519 ) (account uint32, e error) {
1520 var manager *waddrmgr.ScopedKeyManager
1521 manager, e = w.Manager.FetchScopedKeyManager(scope)
1522 if e != nil {
1523 return 0, e
1524 }
1525 e = walletdb.View(
1526 w.db, func(tx walletdb.ReadTx) (e error) {
1527 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1528 account, e = manager.LookupAccount(addrmgrNs, accountName)
1529 return e
1530 },
1531 )
1532 return account, e
1533 }
1534 1535 // AccountName returns the name of an account.
1536 func (w *Wallet) AccountName(
1537 scope waddrmgr.KeyScope, accountNumber uint32,
1538 ) (string, error) {
1539 manager, e := w.Manager.FetchScopedKeyManager(scope)
1540 if e != nil {
1541 return "", e
1542 }
1543 var accountName string
1544 e = walletdb.View(
1545 w.db, func(tx walletdb.ReadTx) (e error) {
1546 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1547 accountName, e = manager.AccountName(addrmgrNs, accountNumber)
1548 return e
1549 },
1550 )
1551 return accountName, e
1552 }
1553 1554 // AccountProperties returns the properties of an account, including address indexes and name. It first fetches the
1555 // desynced information from the address manager, then updates the indexes based on the address pools.
1556 func (w *Wallet) AccountProperties(
1557 scope waddrmgr.KeyScope, acct uint32,
1558 ) (*waddrmgr.AccountProperties, error) {
1559 manager, e := w.Manager.FetchScopedKeyManager(scope)
1560 if e != nil {
1561 return nil, e
1562 }
1563 var props *waddrmgr.AccountProperties
1564 e = walletdb.View(
1565 w.db, func(tx walletdb.ReadTx) (e error) {
1566 waddrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1567 props, e = manager.AccountProperties(waddrmgrNs, acct)
1568 return e
1569 },
1570 )
1571 return props, e
1572 }
1573 1574 // RenameAccount sets the name for an account number to newName.
1575 func (w *Wallet) RenameAccount(
1576 scope waddrmgr.KeyScope, account uint32, newName string,
1577 ) (e error) {
1578 manager, e := w.Manager.FetchScopedKeyManager(scope)
1579 if e != nil {
1580 return e
1581 }
1582 var props *waddrmgr.AccountProperties
1583 e = walletdb.Update(
1584 w.db, func(tx walletdb.ReadWriteTx) (e error) {
1585 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
1586 e = manager.RenameAccount(addrmgrNs, account, newName)
1587 if e != nil {
1588 return e
1589 }
1590 props, e = manager.AccountProperties(addrmgrNs, account)
1591 return e
1592 },
1593 )
1594 if e == nil {
1595 w.NtfnServer.notifyAccountProperties(props)
1596 }
1597 return e
1598 }
1599 1600 // const maxEmptyAccounts = 100
1601 1602 // NextAccount creates the next account and returns its account number. The name must be unique to the account. In order
1603 // to support automatic seed restoring, new accounts may not be created when all of the previous 100 accounts have no
1604 // transaction history (this is a deviation from the BIP0044 spec, which allows no unused account gaps).
1605 func (w *Wallet) NextAccount(scope waddrmgr.KeyScope, name string) (
1606 uint32, error,
1607 ) {
1608 manager, e := w.Manager.FetchScopedKeyManager(scope)
1609 if e != nil {
1610 return 0, e
1611 }
1612 var (
1613 account uint32
1614 props *waddrmgr.AccountProperties
1615 )
1616 e = walletdb.Update(
1617 w.db, func(tx walletdb.ReadWriteTx) (e error) {
1618 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
1619 account, e = manager.NewAccount(addrmgrNs, name)
1620 if e != nil {
1621 return e
1622 }
1623 props, e = manager.AccountProperties(addrmgrNs, account)
1624 return e
1625 },
1626 )
1627 if e != nil {
1628 E.Ln(
1629 "cannot fetch new account properties for notification after"+
1630 " account creation:", e,
1631 )
1632 }
1633 w.NtfnServer.notifyAccountProperties(props)
1634 return account, e
1635 }
1636 1637 // CreditCategory describes the type of wallet transaction output. The category of "sent transactions" (debits) is
1638 // always "send", and is not expressed by this type.
1639 //
1640 // TODO: This is a requirement of the RPC server and should be moved.
1641 type CreditCategory byte
1642 1643 // These constants define the possible credit categories.
1644 const (
1645 CreditReceive CreditCategory = iota
1646 CreditGenerate
1647 CreditImmature
1648 )
1649 1650 // String returns the category as a string. This string may be used as the JSON string for categories as part of
1651 // listtransactions and gettransaction RPC responses.
1652 func (c CreditCategory) String() string {
1653 switch c {
1654 case CreditReceive:
1655 return "receive"
1656 case CreditGenerate:
1657 return "generate"
1658 case CreditImmature:
1659 return "immature"
1660 default:
1661 return "unknown"
1662 }
1663 }
1664 1665 // RecvCategory returns the category of received credit outputs from a transaction record. The passed block chain height
1666 // is used to distinguish immature from mature coinbase outputs.
1667 //
1668 // TODO: This is intended for use by the RPC server and should be moved out of this package at a later time.
1669 func RecvCategory(
1670 details *wtxmgr.TxDetails, syncHeight int32, net *chaincfg.Params,
1671 ) CreditCategory {
1672 if blockchain.IsCoinBaseTx(&details.MsgTx) {
1673 if confirmed(
1674 int32(net.CoinbaseMaturity), details.Block.Height,
1675 syncHeight,
1676 ) {
1677 return CreditGenerate
1678 }
1679 return CreditImmature
1680 }
1681 return CreditReceive
1682 }
1683 1684 // listTransactions creates a object that may be marshalled to a response result for a listtransactions RPC.
1685 //
1686 // TODO: This should be moved to the legacyrpc package.
1687 func listTransactions(
1688 tx walletdb.ReadTx, details *wtxmgr.TxDetails, addrMgr *waddrmgr.Manager,
1689 syncHeight int32, net *chaincfg.Params,
1690 ) []btcjson.ListTransactionsResult {
1691 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
1692 var (
1693 blockHashStr string
1694 blockTime int64
1695 confirmations int64
1696 )
1697 if details.Block.Height != -1 {
1698 blockHashStr = details.Block.Hash.String()
1699 blockTime = details.Block.Time.Unix()
1700 confirmations = int64(confirms(details.Block.Height, syncHeight))
1701 }
1702 results := []btcjson.ListTransactionsResult{}
1703 txHashStr := details.Hash.String()
1704 received := details.Received.Unix()
1705 generated := blockchain.IsCoinBaseTx(&details.MsgTx)
1706 recvCat := RecvCategory(details, syncHeight, net).String()
1707 send := len(details.Debits) != 0
1708 // Fee can only be determined if every input is a debit.
1709 var feeF64 float64
1710 if len(details.Debits) == len(details.MsgTx.TxIn) {
1711 var debitTotal amt.Amount
1712 for _, deb := range details.Debits {
1713 debitTotal += deb.Amount
1714 }
1715 var outputTotal amt.Amount
1716 for _, output := range details.MsgTx.TxOut {
1717 outputTotal += amt.Amount(output.Value)
1718 }
1719 // Note: The actual fee is debitTotal - outputTotal. However, this RPC reports negative numbers for fees, so the
1720 // inverse is calculated.
1721 feeF64 = (outputTotal - debitTotal).ToDUO()
1722 }
1723 outputs:
1724 for i, output := range details.MsgTx.TxOut {
1725 // Determine if this output is a credit, and if so, determine its spentness.
1726 var isCredit bool
1727 var spentCredit bool
1728 for _, cred := range details.Credits {
1729 if cred.Index == uint32(i) {
1730 // Change outputs are ignored.
1731 if cred.Change {
1732 continue outputs
1733 }
1734 isCredit = true
1735 spentCredit = cred.Spent
1736 break
1737 }
1738 }
1739 var address string
1740 var accountName string
1741 _, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript, net)
1742 if len(addrs) == 1 {
1743 addr := addrs[0]
1744 address = addr.EncodeAddress()
1745 mgr, account, e := addrMgr.AddrAccount(addrmgrNs, addrs[0])
1746 if e == nil {
1747 accountName, e = mgr.AccountName(addrmgrNs, account)
1748 if e != nil {
1749 accountName = ""
1750 }
1751 }
1752 }
1753 amountF64 := amt.Amount(output.Value).ToDUO()
1754 blockIndex := int64(details.Block.Height)
1755 result := btcjson.ListTransactionsResult{
1756 // Fields left zeroed:
1757 // InvolvesWatchOnly
1758 // BlockIndex
1759 //
1760 // Fields set below:
1761 // Account (only for non-"send" categories)
1762 // Category
1763 // Amount
1764 // Fee
1765 Address: address,
1766 Vout: uint32(i),
1767 Confirmations: confirmations,
1768 Generated: generated,
1769 BlockHash: blockHashStr,
1770 BlockIndex: blockIndex,
1771 BlockTime: blockTime,
1772 TxID: txHashStr,
1773 WalletConflicts: []string{},
1774 Time: received,
1775 TimeReceived: received,
1776 }
1777 // Add a received/generated/immature result if this is a credit. If the output was spent, create a second result
1778 // under the send category with the inverse of the output amount. It is therefore possible that a single output
1779 // may be included in the results set zero, one, or two times.
1780 //
1781 // Since credits are not saved for outputs that are not controlled by this wallet, all non-credits from
1782 // transactions with debits are grouped under the send category.
1783 if send || spentCredit {
1784 result.Category = "send"
1785 result.Amount = -amountF64
1786 result.Fee = feeF64
1787 results = append(results, result)
1788 }
1789 if isCredit {
1790 result.Account = accountName
1791 result.Category = recvCat
1792 result.Amount = amountF64
1793 result.Fee = 0
1794 results = append(results, result)
1795 }
1796 }
1797 return results
1798 }
1799 1800 // ListSinceBlock returns a slice of objects with details about transactions since the given block. If the block is -1
1801 // then all transactions are included. This is intended to be used for listsinceblock RPC replies.
1802 func (w *Wallet) ListSinceBlock(start, end, syncHeight int32) (
1803 txList []btcjson.ListTransactionsResult, e error,
1804 ) {
1805 txList = []btcjson.ListTransactionsResult{}
1806 e = walletdb.View(
1807 w.db, func(tx walletdb.ReadTx) (e error) {
1808 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1809 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
1810 for _, detail := range details {
1811 jsonResults := listTransactions(
1812 tx, &detail,
1813 w.Manager, syncHeight, w.chainParams,
1814 )
1815 txList = append(txList, jsonResults...)
1816 }
1817 return false, nil
1818 }
1819 return w.TxStore.RangeTransactions(txmgrNs, start, end, rangeFn)
1820 },
1821 )
1822 return
1823 }
1824 1825 // ListTransactions returns a slice of objects with details about a recorded
1826 // transaction. This is intended to be used for listtransactions RPC replies.
1827 func (w *Wallet) ListTransactions(from, count int) (
1828 txList []btcjson.ListTransactionsResult, e error,
1829 ) {
1830 // txList := []btcjson.ListTransactionsResult{}
1831 // T.Ln("ListTransactions", from, count)
1832 if e = walletdb.View(
1833 w.db, func(tx walletdb.ReadTx) (e error) {
1834 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1835 // Get current block. The block height used for calculating the number of tx
1836 // confirmations.
1837 syncBlock := w.Manager.SyncedTo()
1838 D.Ln("synced to", syncBlock)
1839 // Need to skip the first from transactions, and after those, only include the
1840 // next count transactions.
1841 skipped := 0
1842 n := 0
1843 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
1844 // Iterate over transactions at this height in reverse order. This does nothing
1845 // for unmined transactions, which are unsorted, but it will process mined
1846 // transactions in the reverse order they were marked mined.
1847 for i := len(details) - 1; i >= 0; i-- {
1848 if from > skipped {
1849 skipped++
1850 continue
1851 }
1852 n++
1853 if n > count {
1854 return true, nil
1855 }
1856 jsonResults := listTransactions(
1857 tx, &details[i],
1858 w.Manager, syncBlock.Height, w.chainParams,
1859 )
1860 txList = append(txList, jsonResults...)
1861 if len(jsonResults) > 0 {
1862 n++
1863 }
1864 }
1865 return false, nil
1866 }
1867 // Return newer results first by starting at mempool height and working down to
1868 // the genesis block.
1869 return w.TxStore.RangeTransactions(txmgrNs, -1, 0, rangeFn)
1870 },
1871 ); E.Chk(e) {
1872 }
1873 return
1874 }
1875 1876 // ListAddressTransactions returns a slice of objects with details about recorded transactions to or from any address
1877 // belonging to a set. This is intended to be used for listaddresstransactions RPC replies.
1878 func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
1879 txList []btcjson.ListTransactionsResult,
1880 e error,
1881 ) {
1882 txList = []btcjson.ListTransactionsResult{}
1883 e = walletdb.View(
1884 w.db, func(tx walletdb.ReadTx) (e error) {
1885 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1886 // Get current block. The block height used for calculating the number of tx confirmations.
1887 syncBlock := w.Manager.SyncedTo()
1888 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
1889 loopDetails:
1890 for i := range details {
1891 detail := &details[i]
1892 for _, cred := range detail.Credits {
1893 pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
1894 var addrs []btcaddr.Address
1895 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
1896 pkScript, w.chainParams,
1897 )
1898 if e != nil || len(addrs) != 1 {
1899 continue
1900 }
1901 apkh, ok := addrs[0].(*btcaddr.PubKeyHash)
1902 if !ok {
1903 continue
1904 }
1905 _, ok = pkHashes[string(apkh.ScriptAddress())]
1906 if !ok {
1907 continue
1908 }
1909 jsonResults := listTransactions(
1910 tx, detail,
1911 w.Manager, syncBlock.Height, w.chainParams,
1912 )
1913 // if e != nil {
1914 // return false, err
1915 // }
1916 txList = append(txList, jsonResults...)
1917 continue loopDetails
1918 }
1919 }
1920 return false, nil
1921 }
1922 return w.TxStore.RangeTransactions(txmgrNs, 0, -1, rangeFn)
1923 },
1924 )
1925 return txList, e
1926 }
1927 1928 // ListAllTransactions returns a slice of objects with details about a recorded transaction. This is intended to be used
1929 // for listalltransactions RPC replies.
1930 func (w *Wallet) ListAllTransactions() (
1931 txList []btcjson.ListTransactionsResult, e error,
1932 ) {
1933 txList = []btcjson.ListTransactionsResult{}
1934 e = walletdb.View(
1935 w.db, func(tx walletdb.ReadTx) (e error) {
1936 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
1937 // Get current block. The block height used for calculating the number of tx confirmations.
1938 syncBlock := w.Manager.SyncedTo()
1939 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
1940 // Iterate over transactions at this height in reverse order. This does nothing for unmined transactions,
1941 // which are unsorted, but it will process mined transactions in the reverse order they were marked mined.
1942 for i := len(details) - 1; i >= 0; i-- {
1943 jsonResults := listTransactions(
1944 tx, &details[i], w.Manager,
1945 syncBlock.Height, w.chainParams,
1946 )
1947 txList = append(txList, jsonResults...)
1948 }
1949 return false, nil
1950 }
1951 // Return newer results first by starting at mempool height and working down to the genesis block.
1952 return w.TxStore.RangeTransactions(txmgrNs, -1, 0, rangeFn)
1953 },
1954 )
1955 return txList, e
1956 }
1957 1958 // BlockIdentifier identifies a block by either a height or a hash.
1959 type BlockIdentifier struct {
1960 height int32
1961 hash *chainhash.Hash
1962 }
1963 1964 // NewBlockIdentifierFromHeight constructs a BlockIdentifier for a block height.
1965 func NewBlockIdentifierFromHeight(height int32) *BlockIdentifier {
1966 return &BlockIdentifier{height: height}
1967 }
1968 1969 // NewBlockIdentifierFromHash constructs a BlockIdentifier for a block hash.
1970 func NewBlockIdentifierFromHash(hash *chainhash.Hash) *BlockIdentifier {
1971 return &BlockIdentifier{hash: hash}
1972 }
1973 1974 // GetTransactionsResult is the result of the wallet's GetTransactions method. See GetTransactions for more details.
1975 type GetTransactionsResult struct {
1976 MinedTransactions []Block
1977 UnminedTransactions []TransactionSummary
1978 }
1979 1980 // GetTransactions returns transaction results between a starting and ending block. BlockC in the block range may be
1981 // specified by either a height or a hash.
1982 //
1983 // Because this is a possibly lengthy operation, a cancel channel is provided to cancel the task. If this channel
1984 // unblocks, the results created thus far will be returned.
1985 //
1986 // Transaction results are organized by blocks in ascending order and unmined transactions in an unspecified order.
1987 // Mined transactions are saved in a Block structure which records properties about the block.
1988 func (w *Wallet) GetTransactions(
1989 startBlock, endBlock *BlockIdentifier, cancel qu.C,
1990 ) (
1991 res *GetTransactionsResult,
1992 e error,
1993 ) {
1994 var start, end int32 = 0, -1
1995 w.chainClientLock.Lock()
1996 chainClient := w.chainClient
1997 w.chainClientLock.Unlock()
1998 // TODO: Fetching block heights by their hashes is inherently racy because not all block headers are saved but when
1999 // they are for SPV the db can be queried directly without this.
2000 var startResp, endResp rpcclient.FutureGetBlockVerboseResult
2001 if startBlock != nil {
2002 if startBlock.hash == nil {
2003 start = startBlock.height
2004 } else {
2005 if chainClient == nil {
2006 return nil, errors.New("no chain server client")
2007 }
2008 switch client := chainClient.(type) {
2009 case *chainclient.RPCClient:
2010 startResp = client.GetBlockVerboseTxAsync(startBlock.hash)
2011 case *chainclient.BitcoindClient:
2012 start, e = client.GetBlockHeight(startBlock.hash)
2013 if e != nil {
2014 return nil, e
2015 }
2016 // case *chainclient.NeutrinoClient:
2017 // start, e = client.GetBlockHeight(startBlock.hash)
2018 // if e != nil {
2019 // return nil, e
2020 // }
2021 }
2022 }
2023 }
2024 if endBlock != nil {
2025 if endBlock.hash == nil {
2026 end = endBlock.height
2027 } else {
2028 if chainClient == nil {
2029 return nil, errors.New("no chain server client")
2030 }
2031 switch client := chainClient.(type) {
2032 case *chainclient.RPCClient:
2033 endResp = client.GetBlockVerboseTxAsync(endBlock.hash)
2034 // case *chainclient.NeutrinoClient:
2035 // end, e = client.GetBlockHeight(endBlock.hash)
2036 // if e != nil {
2037 // return nil, e
2038 // }
2039 }
2040 }
2041 }
2042 var resp *btcjson.GetBlockVerboseResult
2043 if startResp != nil {
2044 resp, e = startResp.Receive()
2045 if e != nil {
2046 return nil, e
2047 }
2048 start = int32(resp.Height)
2049 }
2050 if endResp != nil {
2051 resp, e = endResp.Receive()
2052 if e != nil {
2053 return nil, e
2054 }
2055 end = int32(resp.Height)
2056 }
2057 res = &GetTransactionsResult{}
2058 e = walletdb.View(
2059 w.db, func(dbtx walletdb.ReadTx) (e error) {
2060 txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
2061 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
2062 // TODO: probably should make RangeTransactions not reuse the
2063 // details backing array memory.
2064 dets := make([]wtxmgr.TxDetails, len(details))
2065 copy(dets, details)
2066 details = dets
2067 txs := make([]TransactionSummary, 0, len(details))
2068 for i := range details {
2069 txs = append(txs, makeTxSummary(dbtx, w, &details[i]))
2070 }
2071 if details[0].Block.Height != -1 {
2072 blockHash := details[0].Block.Hash
2073 res.MinedTransactions = append(
2074 res.MinedTransactions, Block{
2075 Hash: &blockHash,
2076 Height: details[0].Block.Height,
2077 Timestamp: details[0].Block.Time.Unix(),
2078 Transactions: txs,
2079 },
2080 )
2081 } else {
2082 res.UnminedTransactions = txs
2083 }
2084 select {
2085 case <-cancel.Wait():
2086 return true, nil
2087 default:
2088 return false, nil
2089 }
2090 }
2091 return w.TxStore.RangeTransactions(txmgrNs, start, end, rangeFn)
2092 },
2093 )
2094 return res, e
2095 }
2096 2097 // AccountResult is a single account result for the AccountsResult type.
2098 type AccountResult struct {
2099 waddrmgr.AccountProperties
2100 TotalBalance amt.Amount
2101 }
2102 2103 // AccountsResult is the result of the wallet's Accounts method. See that method for more details.
2104 type AccountsResult struct {
2105 Accounts []AccountResult
2106 CurrentBlockHash *chainhash.Hash
2107 CurrentBlockHeight int32
2108 }
2109 2110 // Accounts returns the current names, numbers, and total balances of all accounts in the wallet restricted to a
2111 // particular key scope. The current chain tip is included in the result for atomicity reasons.
2112 //
2113 // TODO(jrick): Is the chain tip really needed, since only the total balances are included?
2114 func (w *Wallet) Accounts(scope waddrmgr.KeyScope) (*AccountsResult, error) {
2115 manager, e := w.Manager.FetchScopedKeyManager(scope)
2116 if e != nil {
2117 return nil, e
2118 }
2119 var (
2120 accounts []AccountResult
2121 syncBlockHash *chainhash.Hash
2122 syncBlockHeight int32
2123 )
2124 e = walletdb.View(
2125 w.db, func(tx walletdb.ReadTx) (e error) {
2126 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2127 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2128 syncBlock := w.Manager.SyncedTo()
2129 syncBlockHash = &syncBlock.Hash
2130 syncBlockHeight = syncBlock.Height
2131 unspent, e := w.TxStore.UnspentOutputs(txmgrNs)
2132 if e != nil {
2133 return e
2134 }
2135 e = manager.ForEachAccount(
2136 addrmgrNs, func(acct uint32) (e error) {
2137 props, e := manager.AccountProperties(addrmgrNs, acct)
2138 if e != nil {
2139 return e
2140 }
2141 accounts = append(
2142 accounts, AccountResult{
2143 AccountProperties: *props,
2144 // TotalBalance set below
2145 },
2146 )
2147 return nil
2148 },
2149 )
2150 if e != nil {
2151 return e
2152 }
2153 m := make(map[uint32]*amt.Amount)
2154 for i := range accounts {
2155 a := &accounts[i]
2156 m[a.AccountNumber] = &a.TotalBalance
2157 }
2158 for i := range unspent {
2159 output := unspent[i]
2160 var outputAcct uint32
2161 var addrs []btcaddr.Address
2162 _, addrs, _, e = txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams)
2163 if e == nil && len(addrs) > 0 {
2164 _, outputAcct, e = w.Manager.AddrAccount(addrmgrNs, addrs[0])
2165 }
2166 if e == nil {
2167 amt, ok := m[outputAcct]
2168 if ok {
2169 *amt += output.Amount
2170 }
2171 }
2172 }
2173 return nil
2174 },
2175 )
2176 return &AccountsResult{
2177 Accounts: accounts,
2178 CurrentBlockHash: syncBlockHash,
2179 CurrentBlockHeight: syncBlockHeight,
2180 },
2181 e
2182 }
2183 2184 // AccountBalanceResult is a single result for the Wallet.AccountBalances method.
2185 type AccountBalanceResult struct {
2186 AccountNumber uint32
2187 AccountName string
2188 AccountBalance amt.Amount
2189 }
2190 2191 // AccountBalances returns all accounts in the wallet and their balances. Balances are determined by excluding
2192 // transactions that have not met requiredConfs confirmations.
2193 func (w *Wallet) AccountBalances(
2194 scope waddrmgr.KeyScope,
2195 requiredConfs int32,
2196 ) ([]AccountBalanceResult, error) {
2197 manager, e := w.Manager.FetchScopedKeyManager(scope)
2198 if e != nil {
2199 return nil, e
2200 }
2201 var results []AccountBalanceResult
2202 e = walletdb.View(
2203 w.db, func(tx walletdb.ReadTx) (e error) {
2204 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2205 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2206 syncBlock := w.Manager.SyncedTo()
2207 // Fill out all account info except for the balances.
2208 lastAcct, e := manager.LastAccount(addrmgrNs)
2209 if e != nil {
2210 return e
2211 }
2212 results = make([]AccountBalanceResult, lastAcct+2)
2213 for i := range results[:len(results)-1] {
2214 var accountName string
2215 accountName, e = manager.AccountName(addrmgrNs, uint32(i))
2216 if e != nil {
2217 return e
2218 }
2219 results[i].AccountNumber = uint32(i)
2220 results[i].AccountName = accountName
2221 }
2222 results[len(results)-1].AccountNumber = waddrmgr.ImportedAddrAccount
2223 results[len(results)-1].AccountName = waddrmgr.ImportedAddrAccountName
2224 // Fetch all unspent outputs, and iterate over them tallying each account's balance where the output script pays
2225 // to an account address and the required number of confirmations is met.
2226 unspentOutputs, e := w.TxStore.UnspentOutputs(txmgrNs)
2227 if e != nil {
2228 return e
2229 }
2230 for i := range unspentOutputs {
2231 output := &unspentOutputs[i]
2232 if !confirmed(requiredConfs, output.Height, syncBlock.Height) {
2233 continue
2234 }
2235 if output.FromCoinBase && !confirmed(
2236 int32(w.ChainParams().CoinbaseMaturity),
2237 output.Height, syncBlock.Height,
2238 ) {
2239 continue
2240 }
2241 var addrs []btcaddr.Address
2242 _, addrs, _, e = txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams)
2243 if e != nil || len(addrs) == 0 {
2244 continue
2245 }
2246 outputAcct, e := manager.AddrAccount(addrmgrNs, addrs[0])
2247 if e != nil {
2248 continue
2249 }
2250 switch {
2251 case outputAcct == waddrmgr.ImportedAddrAccount:
2252 results[len(results)-1].AccountBalance += output.Amount
2253 case outputAcct > lastAcct:
2254 return errors.New(
2255 "waddrmgr.Manager.AddrAccount returned account " +
2256 "beyond recorded last account",
2257 )
2258 default:
2259 results[outputAcct].AccountBalance += output.Amount
2260 }
2261 }
2262 return nil
2263 },
2264 )
2265 return results, e
2266 }
2267 2268 // creditSlice satisfies the sort.Interface interface to provide sorting transaction credits from oldest to newest.
2269 // Credits with the same receive time and mined in the same block are not guaranteed to be sorted by the order they
2270 // appear in the block. Credits from the same transaction are sorted by output index.
2271 type creditSlice []wtxmgr.Credit
2272 2273 func (s creditSlice) Len() int {
2274 return len(s)
2275 }
2276 func (s creditSlice) Less(i, j int) bool {
2277 switch {
2278 // If both credits are from the same tx, txsort by output index.
2279 case s[i].OutPoint.Hash == s[j].OutPoint.Hash:
2280 return s[i].OutPoint.Index < s[j].OutPoint.Index
2281 // If both transactions are unmined, txsort by their received date.
2282 case s[i].Height == -1 && s[j].Height == -1:
2283 return s[i].Received.Before(s[j].Received)
2284 // Unmined (newer) txs always come last.
2285 case s[i].Height == -1:
2286 return false
2287 case s[j].Height == -1:
2288 return true
2289 // If both txs are mined in different blocks, txsort by block height.
2290 default:
2291 return s[i].Height < s[j].Height
2292 }
2293 }
2294 func (s creditSlice) Swap(i, j int) {
2295 s[i], s[j] = s[j], s[i]
2296 }
2297 2298 // ListUnspent returns a slice of objects representing the unspent wallet transactions fitting the given criteria. The
2299 // confirmations will be more than minconf, less than maxconf and if addresses is populated only the addresses contained
2300 // within it will be considered. If we know nothing about a transaction an empty array will be returned.
2301 func (w *Wallet) ListUnspent(
2302 minconf, maxconf int32,
2303 addresses map[string]struct{},
2304 ) (results []*btcjson.ListUnspentResult, e error) {
2305 e = walletdb.View(
2306 w.db, func(tx walletdb.ReadTx) (e error) {
2307 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2308 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2309 syncBlock := w.Manager.SyncedTo()
2310 filter := len(addresses) != 0
2311 unspent, e := w.TxStore.UnspentOutputs(txmgrNs)
2312 if e != nil {
2313 return e
2314 }
2315 sort.Sort(sort.Reverse(creditSlice(unspent)))
2316 defaultAccountName := "default"
2317 results = make([]*btcjson.ListUnspentResult, 0, len(unspent))
2318 for i := range unspent {
2319 output := unspent[i]
2320 // Outputs with fewer confirmations than the minimum or more confs than the maximum are excluded.
2321 confs := confirms(output.Height, syncBlock.Height)
2322 if confs < minconf || confs > maxconf {
2323 continue
2324 }
2325 // Only mature coinbase outputs are included.
2326 if output.FromCoinBase {
2327 target := int32(w.ChainParams().CoinbaseMaturity)
2328 if !confirmed(target, output.Height, syncBlock.Height) {
2329 continue
2330 }
2331 }
2332 // Exclude locked outputs from the result set.
2333 if w.LockedOutpoint(output.OutPoint) {
2334 continue
2335 }
2336 // Lookup the associated account for the output. Use the default account name in case there is no associated
2337 // account for some reason, although this should never happen.
2338 //
2339 // This will be unnecessary once transactions and outputs are grouped under the associated account in the
2340 // db.
2341 acctName := defaultAccountName
2342 var sc txscript.ScriptClass
2343 var addrs []btcaddr.Address
2344 sc, addrs, _, e = txscript.ExtractPkScriptAddrs(
2345 output.PkScript, w.chainParams,
2346 )
2347 if e != nil {
2348 continue
2349 }
2350 if len(addrs) > 0 {
2351 smgr, acct, e := w.Manager.AddrAccount(addrmgrNs, addrs[0])
2352 if e == nil {
2353 s, e := smgr.AccountName(addrmgrNs, acct)
2354 if e == nil {
2355 acctName = s
2356 }
2357 }
2358 }
2359 if filter {
2360 for _, addr := range addrs {
2361 _, ok := addresses[addr.EncodeAddress()]
2362 if ok {
2363 goto include
2364 }
2365 }
2366 continue
2367 }
2368 include:
2369 // At the moment watch-only addresses are not supported, so all recorded outputs that are not multisig are
2370 // "spendable". Multisig outputs are only "spendable" if all keys are controlled by this wallet.
2371 //
2372 // TODO: Each case will need updates when watch-only addrs is added. For P2PK, P2PKH, and P2SH, the address
2373 // must be looked up and not be watching-only. For multisig, all pubkeys must belong to the manager with the
2374 // associated private key (currently it only checks whether the pubkey exists, since the private key is
2375 // required at the moment).
2376 var spendable bool
2377 scSwitch:
2378 switch sc {
2379 case txscript.PubKeyHashTy:
2380 spendable = true
2381 case txscript.PubKeyTy:
2382 spendable = true
2383 // case txscript.WitnessV0ScriptHashTy:
2384 // spendable = true
2385 // case txscript.WitnessV0PubKeyHashTy:
2386 // spendable = true
2387 case txscript.MultiSigTy:
2388 for _, a := range addrs {
2389 _, e := w.Manager.Address(addrmgrNs, a)
2390 if e == nil {
2391 continue
2392 }
2393 if waddrmgr.IsError(e, waddrmgr.ErrAddressNotFound) {
2394 break scSwitch
2395 }
2396 return e
2397 }
2398 spendable = true
2399 }
2400 result := &btcjson.ListUnspentResult{
2401 TxID: output.OutPoint.Hash.String(),
2402 Vout: output.OutPoint.Index,
2403 Account: acctName,
2404 ScriptPubKey: hex.EncodeToString(output.PkScript),
2405 Amount: output.Amount.ToDUO(),
2406 Confirmations: int64(confs),
2407 Spendable: spendable,
2408 }
2409 // BUG: this should be a JSON array so that all addresses can be included, or removed (and the caller
2410 // extracts addresses from the pkScript).
2411 if len(addrs) > 0 {
2412 result.Address = addrs[0].EncodeAddress()
2413 }
2414 results = append(results, result)
2415 }
2416 return nil
2417 },
2418 )
2419 return results, e
2420 }
2421 2422 // DumpPrivKeys returns the WIF-encoded private keys for all addresses with private keys in a wallet.
2423 func (w *Wallet) DumpPrivKeys() (privkeys []string, e error) {
2424 e = walletdb.View(
2425 w.db, func(tx walletdb.ReadTx) (e error) {
2426 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2427 // Iterate over each active address, appending the private key to privkeys.
2428 return w.Manager.ForEachActiveAddress(
2429 addrmgrNs, func(addr btcaddr.Address) (e error) {
2430 var ma waddrmgr.ManagedAddress
2431 ma, e = w.Manager.Address(addrmgrNs, addr)
2432 if e != nil {
2433 return e
2434 }
2435 // Only those addresses with keys needed.
2436 pka, ok := ma.(waddrmgr.ManagedPubKeyAddress)
2437 if !ok {
2438 return nil
2439 }
2440 var wif *util.WIF
2441 wif, e = pka.ExportPrivKey()
2442 if e != nil {
2443 // It would be nice to zero out the array here. However, since strings in go are immutable, and we have
2444 // no control over the caller I don't think we can. :(
2445 return e
2446 }
2447 privkeys = append(privkeys, wif.String())
2448 return nil
2449 },
2450 )
2451 },
2452 )
2453 return privkeys, e
2454 }
2455 2456 // DumpWIFPrivateKey returns the WIF encoded private key for a single wallet address.
2457 func (w *Wallet) DumpWIFPrivateKey(addr btcaddr.Address) (
2458 address string, e error,
2459 ) {
2460 var maddr waddrmgr.ManagedAddress
2461 e = walletdb.View(
2462 w.db, func(tx walletdb.ReadTx) (e error) {
2463 waddrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2464 // Get private key from wallet if it exists.
2465 maddr, e = w.Manager.Address(waddrmgrNs, addr)
2466 return e
2467 },
2468 )
2469 if e != nil {
2470 return "", e
2471 }
2472 pka, ok := maddr.(waddrmgr.ManagedPubKeyAddress)
2473 if !ok {
2474 return "", fmt.Errorf("address %s is not a key type", addr)
2475 }
2476 wif, e := pka.ExportPrivKey()
2477 if e != nil {
2478 return "", e
2479 }
2480 return wif.String(), nil
2481 }
2482 2483 // ImportPrivateKey imports a private key to the wallet and writes the new wallet to disk.
2484 func (w *Wallet) ImportPrivateKey(
2485 scope waddrmgr.KeyScope, wif *util.WIF,
2486 bs *waddrmgr.BlockStamp, rescan bool,
2487 ) (string, error) {
2488 manager, e := w.Manager.FetchScopedKeyManager(scope)
2489 if e != nil {
2490 return "", e
2491 }
2492 // The starting block for the key is the genesis block unless otherwise specified.
2493 var newBirthday time.Time
2494 if bs == nil {
2495 bs = &waddrmgr.BlockStamp{
2496 Hash: *w.chainParams.GenesisHash,
2497 Height: 0,
2498 }
2499 } else {
2500 // Only update the new birthday time from default value if we actually have timestamp info in the header.
2501 var header *wire.BlockHeader
2502 header, e = w.chainClient.GetBlockHeader(&bs.Hash)
2503 if e == nil {
2504 newBirthday = header.Timestamp
2505 }
2506 }
2507 // Attempt to import private key into wallet.
2508 var addr btcaddr.Address
2509 var props *waddrmgr.AccountProperties
2510 e = walletdb.Update(
2511 w.db, func(tx walletdb.ReadWriteTx) (e error) {
2512 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
2513 maddr, e := manager.ImportPrivateKey(addrmgrNs, wif, bs)
2514 if e != nil {
2515 return e
2516 }
2517 addr = maddr.Address()
2518 props, e = manager.AccountProperties(
2519 addrmgrNs, waddrmgr.ImportedAddrAccount,
2520 )
2521 if e != nil {
2522 return e
2523 }
2524 return w.Manager.SetBirthday(addrmgrNs, newBirthday)
2525 },
2526 )
2527 if e != nil {
2528 return "", e
2529 }
2530 // Rescan blockchain for transactions with txout scripts paying to the imported address.
2531 if rescan {
2532 job := &RescanJob{
2533 Addrs: []btcaddr.Address{addr},
2534 OutPoints: nil,
2535 BlockStamp: *bs,
2536 }
2537 // Submit rescan job and log when the import has completed. Do not block on finishing the rescan. The rescan
2538 // success or failure is logged elsewhere, and the channel is not required to be read, so discard the return
2539 // value.
2540 _ = w.SubmitRescan(job)
2541 } else {
2542 e := w.chainClient.NotifyReceived([]btcaddr.Address{addr})
2543 if e != nil {
2544 return "", fmt.Errorf(
2545 "failed to subscribe for address ntfns for "+
2546 "address %s: %s", addr.EncodeAddress(), e,
2547 )
2548 }
2549 }
2550 addrStr := addr.EncodeAddress()
2551 I.Ln("imported payment address", addrStr)
2552 w.NtfnServer.notifyAccountProperties(props)
2553 // Return the payment address string of the imported private key.
2554 return addrStr, nil
2555 }
2556 2557 // LockedOutpoint returns whether an outpoint has been marked as locked and should not be used as an input for created
2558 // transactions.
2559 func (w *Wallet) LockedOutpoint(op wire.OutPoint) bool {
2560 _, locked := w.lockedOutpoints[op]
2561 return locked
2562 }
2563 2564 // LockOutpoint marks an outpoint as locked, that is, it should not be used as an input for newly created transactions.
2565 func (w *Wallet) LockOutpoint(op wire.OutPoint) {
2566 w.lockedOutpoints[op] = struct{}{}
2567 }
2568 2569 // UnlockOutpoint marks an outpoint as unlocked, that is, it may be used as an input for newly created transactions.
2570 func (w *Wallet) UnlockOutpoint(op wire.OutPoint) {
2571 delete(w.lockedOutpoints, op)
2572 }
2573 2574 // ResetLockedOutpoints resets the set of locked outpoints so all may be used as inputs for new transactions.
2575 func (w *Wallet) ResetLockedOutpoints() {
2576 w.lockedOutpoints = map[wire.OutPoint]struct{}{}
2577 }
2578 2579 // LockedOutpoints returns a slice of currently locked outpoints. This is intended to be used by marshaling the result
2580 // as a JSON array for listlockunspent RPC results.
2581 func (w *Wallet) LockedOutpoints() []btcjson.TransactionInput {
2582 locked := make([]btcjson.TransactionInput, len(w.lockedOutpoints))
2583 i := 0
2584 for op := range w.lockedOutpoints {
2585 locked[i] = btcjson.TransactionInput{
2586 Txid: op.Hash.String(),
2587 Vout: op.Index,
2588 }
2589 i++
2590 }
2591 return locked
2592 }
2593 2594 // resendUnminedTxs iterates through all transactions that spend from wallet credits that are not known to have been
2595 // mined into a block, and attempts to send each to the chain server for relay.
2596 func (w *Wallet) resendUnminedTxs() {
2597 chainClient, e := w.requireChainClient()
2598 if e != nil {
2599 E.Ln("no chain server available to resend unmined transactions", e)
2600 return
2601 }
2602 var txs []*wire.MsgTx
2603 e = walletdb.View(
2604 w.db, func(tx walletdb.ReadTx) (e error) {
2605 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2606 txs, e = w.TxStore.UnminedTxs(txmgrNs)
2607 return e
2608 },
2609 )
2610 if e != nil {
2611 E.Ln("cannot load unmined transactions for resending:", e)
2612 return
2613 }
2614 for _, tx := range txs {
2615 resp, e := chainClient.SendRawTransaction(tx, false)
2616 if e != nil {
2617 D.F(
2618 "could not resend transaction %v: %v %s",
2619 tx.TxHash(), e,
2620 )
2621 // We'll only stop broadcasting transactions if we detect that the output has already been fully spent, is
2622 // an orphan, or is conflicting with another transaction.
2623 //
2624 // TODO(roasbeef): SendRawTransaction needs to return concrete error types, no need for string matching
2625 switch {
2626 // The following are errors returned from pod's mempool.
2627 case strings.Contains(e.Error(), "spent"):
2628 case strings.Contains(e.Error(), "orphan"):
2629 case strings.Contains(e.Error(), "conflict"):
2630 case strings.Contains(e.Error(), "already exists"):
2631 case strings.Contains(e.Error(), "negative"):
2632 // The following errors are returned from bitcoind's
2633 // mempool.
2634 case strings.Contains(e.Error(), "Missing inputs"):
2635 case strings.Contains(e.Error(), "already in block chain"):
2636 case strings.Contains(e.Error(), "fee not met"):
2637 default:
2638 continue
2639 }
2640 // As the transaction was rejected, we'll attempt to remove the unmined transaction all together. Otherwise,
2641 // we'll keep attempting to rebroadcast this, and we may be computing our balance incorrectly if this tx
2642 // credits or debits to us.
2643 tt := tx
2644 e := walletdb.Update(
2645 w.db, func(dbTx walletdb.ReadWriteTx) (e error) {
2646 txmgrNs := dbTx.ReadWriteBucket(wtxmgrNamespaceKey)
2647 txRec, e := wtxmgr.NewTxRecordFromMsgTx(
2648 tt, time.Now(),
2649 )
2650 if e != nil {
2651 return e
2652 }
2653 return w.TxStore.RemoveUnminedTx(txmgrNs, txRec)
2654 },
2655 )
2656 if e != nil {
2657 W.F(
2658 "unable to remove conflicting tx %v: %v %s", tt.TxHash(),
2659 e,
2660 )
2661 continue
2662 }
2663 I.C(
2664 func() string {
2665 return "removed conflicting tx:" + spew.Sdump(tt) + " "
2666 },
2667 )
2668 continue
2669 }
2670 D.Ln("resent unmined transaction", resp)
2671 }
2672 }
2673 2674 // SortedActivePaymentAddresses returns a slice of all active payment addresses in a wallet.
2675 func (w *Wallet) SortedActivePaymentAddresses() ([]string, error) {
2676 var addrStrs []string
2677 e := walletdb.View(
2678 w.db, func(tx walletdb.ReadTx) (e error) {
2679 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2680 return w.Manager.ForEachActiveAddress(
2681 addrmgrNs, func(addr btcaddr.Address) (e error) {
2682 addrStrs = append(addrStrs, addr.EncodeAddress())
2683 return nil
2684 },
2685 )
2686 },
2687 )
2688 if e != nil {
2689 return nil, e
2690 }
2691 sort.Strings(addrStrs)
2692 return addrStrs, nil
2693 }
2694 2695 // NewAddress returns the next external chained address for a wallet.
2696 func (w *Wallet) NewAddress(
2697 account uint32, scope waddrmgr.KeyScope,
2698 nochain bool,
2699 ) (addr btcaddr.Address, e error) {
2700 var (
2701 chainClient chainclient.Interface
2702 props *waddrmgr.AccountProperties
2703 )
2704 if !nochain {
2705 chainClient, e = w.requireChainClient()
2706 if e != nil {
2707 return nil, e
2708 }
2709 }
2710 e = walletdb.Update(
2711 w.db, func(tx walletdb.ReadWriteTx) (e error) {
2712 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
2713 addr, props, e = w.newAddress(addrmgrNs, account, scope)
2714 return e
2715 },
2716 )
2717 if e != nil {
2718 return nil, e
2719 }
2720 if !nochain {
2721 // Notify the rpc server about the newly created address.
2722 e = chainClient.NotifyReceived([]btcaddr.Address{addr})
2723 if e != nil {
2724 return nil, e
2725 }
2726 w.NtfnServer.notifyAccountProperties(props)
2727 }
2728 return addr, nil
2729 }
2730 func (w *Wallet) newAddress(
2731 addrmgrNs walletdb.ReadWriteBucket, account uint32,
2732 scope waddrmgr.KeyScope,
2733 ) (btcaddr.Address, *waddrmgr.AccountProperties, error) {
2734 manager, e := w.Manager.FetchScopedKeyManager(scope)
2735 if e != nil {
2736 return nil, nil, e
2737 }
2738 // Get next address from wallet.
2739 var addrs []waddrmgr.ManagedAddress
2740 if addrs, e = manager.NextExternalAddresses(addrmgrNs, account, 1); E.Chk(e) {
2741 return nil, nil, e
2742 }
2743 var props *waddrmgr.AccountProperties
2744 if props, e = manager.AccountProperties(addrmgrNs, account); E.Chk(e) {
2745 E.Ln(
2746 "cannot fetch account properties for notification after deriving next external address:",
2747 e,
2748 )
2749 return nil, nil, e
2750 }
2751 return addrs[0].Address(), props, nil
2752 }
2753 2754 // NewChangeAddress returns a new change address for a wallet.
2755 func (w *Wallet) NewChangeAddress(
2756 account uint32,
2757 scope waddrmgr.KeyScope,
2758 ) (btcaddr.Address, error) {
2759 chainClient, e := w.requireChainClient()
2760 if e != nil {
2761 return nil, e
2762 }
2763 var addr btcaddr.Address
2764 e = walletdb.Update(
2765 w.db, func(tx walletdb.ReadWriteTx) (e error) {
2766 addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
2767 addr, e = w.newChangeAddress(addrmgrNs, account)
2768 return e
2769 },
2770 )
2771 if e != nil {
2772 return nil, e
2773 }
2774 // Notify the rpc server about the newly created address.
2775 e = chainClient.NotifyReceived([]btcaddr.Address{addr})
2776 if e != nil {
2777 return nil, e
2778 }
2779 return addr, nil
2780 }
2781 func (w *Wallet) newChangeAddress(
2782 addrmgrNs walletdb.ReadWriteBucket,
2783 account uint32,
2784 ) (btcaddr.Address, error) {
2785 // As we're making a change address, we'll fetch the type of manager that is able to make p2wkh output as they're
2786 // the most efficient.
2787 scopes := w.Manager.ScopesForExternalAddrType(
2788 waddrmgr.PubKeyHash,
2789 )
2790 manager, e := w.Manager.FetchScopedKeyManager(scopes[0])
2791 if e != nil {
2792 return nil, e
2793 }
2794 // Get next chained change address from wallet for account.
2795 addrs, e := manager.NextInternalAddresses(addrmgrNs, account, 1)
2796 if e != nil {
2797 return nil, e
2798 }
2799 return addrs[0].Address(), nil
2800 }
2801 2802 // confirmed checks whether a transaction at height txHeight has met minconf confirmations for a blockchain at height
2803 // curHeight.
2804 func confirmed(minconf, txHeight, curHeight int32) bool {
2805 return confirms(txHeight, curHeight) >= minconf
2806 }
2807 2808 // confirms returns the number of confirmations for a transaction in a block at height txHeight (or -1 for an
2809 // unconfirmed tx) given the chain height curHeight.
2810 func confirms(txHeight, curHeight int32) int32 {
2811 switch {
2812 case txHeight == -1, txHeight > curHeight:
2813 return 0
2814 default:
2815 return curHeight - txHeight + 1
2816 }
2817 }
2818 2819 // AccountTotalReceivedResult is a single result for the Wallet.TotalReceivedForAccounts method.
2820 type AccountTotalReceivedResult struct {
2821 AccountNumber uint32
2822 AccountName string
2823 TotalReceived amt.Amount
2824 LastConfirmation int32
2825 }
2826 2827 // TotalReceivedForAccounts iterates through a wallet's transaction history, returning the total amount of Bitcoin
2828 // received for all accounts.
2829 func (w *Wallet) TotalReceivedForAccounts(
2830 scope waddrmgr.KeyScope,
2831 minConf int32,
2832 ) ([]AccountTotalReceivedResult, error) {
2833 manager, e := w.Manager.FetchScopedKeyManager(scope)
2834 if e != nil {
2835 return nil, e
2836 }
2837 var results []AccountTotalReceivedResult
2838 e = walletdb.View(
2839 w.db, func(tx walletdb.ReadTx) (e error) {
2840 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
2841 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2842 syncBlock := w.Manager.SyncedTo()
2843 e = manager.ForEachAccount(
2844 addrmgrNs, func(account uint32) (e error) {
2845 accountName, e := manager.AccountName(addrmgrNs, account)
2846 if e != nil {
2847 return e
2848 }
2849 results = append(
2850 results, AccountTotalReceivedResult{
2851 AccountNumber: account,
2852 AccountName: accountName,
2853 },
2854 )
2855 return nil
2856 },
2857 )
2858 if e != nil {
2859 return e
2860 }
2861 var stopHeight int32
2862 if minConf > 0 {
2863 stopHeight = syncBlock.Height - minConf + 1
2864 } else {
2865 stopHeight = -1
2866 }
2867 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
2868 for i := range details {
2869 detail := &details[i]
2870 for _, cred := range detail.Credits {
2871 pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
2872 var outputAcct uint32
2873 var addrs []btcaddr.Address
2874 _, addrs, _, e = txscript.ExtractPkScriptAddrs(pkScript, w.chainParams)
2875 if e == nil && len(addrs) > 0 {
2876 _, outputAcct, e = w.Manager.AddrAccount(addrmgrNs, addrs[0])
2877 }
2878 if e == nil {
2879 acctIndex := int(outputAcct)
2880 if outputAcct == waddrmgr.ImportedAddrAccount {
2881 acctIndex = len(results) - 1
2882 }
2883 res := &results[acctIndex]
2884 res.TotalReceived += cred.Amount
2885 res.LastConfirmation = confirms(
2886 detail.Block.Height, syncBlock.Height,
2887 )
2888 }
2889 }
2890 }
2891 return false, nil
2892 }
2893 return w.TxStore.RangeTransactions(txmgrNs, 0, stopHeight, rangeFn)
2894 },
2895 )
2896 return results, e
2897 }
2898 2899 // TotalReceivedForAddr iterates through a wallet's transaction history, returning the total amount of bitcoins received
2900 // for a single wallet address.
2901 func (w *Wallet) TotalReceivedForAddr(
2902 addr btcaddr.Address, minConf int32,
2903 ) (amount amt.Amount, e error) {
2904 e = walletdb.View(
2905 w.db, func(tx walletdb.ReadTx) (e error) {
2906 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
2907 syncBlock := w.Manager.SyncedTo()
2908 var (
2909 addrStr = addr.EncodeAddress()
2910 stopHeight int32
2911 )
2912 if minConf > 0 {
2913 stopHeight = syncBlock.Height - minConf + 1
2914 } else {
2915 stopHeight = -1
2916 }
2917 rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
2918 for i := range details {
2919 detail := &details[i]
2920 for _, cred := range detail.Credits {
2921 pkScript := detail.MsgTx.TxOut[cred.Index].PkScript
2922 var addrs []btcaddr.Address
2923 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
2924 pkScript,
2925 w.chainParams,
2926 )
2927 // An error creating addresses from the output script only indicates a non-standard script, so
2928 // ignore this credit.
2929 if e != nil {
2930 continue
2931 }
2932 for _, a := range addrs {
2933 if addrStr == a.EncodeAddress() {
2934 amount += cred.Amount
2935 break
2936 }
2937 }
2938 }
2939 }
2940 return false, nil
2941 }
2942 return w.TxStore.RangeTransactions(txmgrNs, 0, stopHeight, rangeFn)
2943 },
2944 )
2945 return amount, e
2946 }
2947 2948 // SendOutputs creates and sends payment transactions. It returns the transaction hash upon success.
2949 func (w *Wallet) SendOutputs(
2950 outputs []*wire.TxOut, account uint32,
2951 minconf int32, satPerKb amt.Amount,
2952 ) (*chainhash.Hash, error) {
2953 // Ensure the outputs to be created adhere to the network's consensus rules.
2954 for _, output := range outputs {
2955 if e := txrules.CheckOutput(output, satPerKb); E.Chk(e) {
2956 return nil, e
2957 }
2958 }
2959 // Create the transaction and broadcast it to the network. The transaction will be added to the database in order to
2960 // ensure that we continue to re-broadcast the transaction upon restarts until it has been confirmed.
2961 createdTx, e := w.CreateSimpleTx(account, outputs, minconf, satPerKb)
2962 if e != nil {
2963 return nil, e
2964 }
2965 D.S(createdTx)
2966 return w.publishTransaction(createdTx.Tx)
2967 }
2968 2969 // SignatureError records the underlying error when validating a transaction input signature.
2970 type SignatureError struct {
2971 InputIndex uint32
2972 Error error
2973 }
2974 2975 // SignTransaction uses secrets of the wallet, as well as additional secrets
2976 // passed in by the caller, to create and add input signatures to a transaction.
2977 //
2978 // Transaction input script validation is used to confirm that all signatures
2979 // are valid. For any invalid input, a SignatureError is added to the returns.
2980 // The final error return is reserved for unexpected or fatal errors, such as
2981 // being unable to determine a previous output script to redeem.
2982 //
2983 // The transaction pointed to by tx is modified by this function.
2984 func (w *Wallet) SignTransaction(
2985 tx *wire.MsgTx, hashType txscript.SigHashType,
2986 additionalPrevScripts map[wire.OutPoint][]byte,
2987 additionalKeysByAddress map[string]*util.WIF,
2988 p2shRedeemScriptsByAddress map[string][]byte,
2989 ) (signErrors []SignatureError, e error) {
2990 I.Ln("signing transaction")
2991 I.S(tx)
2992 e = walletdb.View(
2993 w.db, func(dbtx walletdb.ReadTx) (e error) {
2994 addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
2995 txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
2996 for i, txIn := range tx.TxIn {
2997 prevOutScript, ok := additionalPrevScripts[txIn.PreviousOutPoint]
2998 if !ok {
2999 prevHash := &txIn.PreviousOutPoint.Hash
3000 prevIndex := txIn.PreviousOutPoint.Index
3001 var txDetails *wtxmgr.TxDetails
3002 txDetails, e = w.TxStore.TxDetails(txmgrNs, prevHash)
3003 if e != nil {
3004 return fmt.Errorf(
3005 "cannot query previous transaction details for %v: %v",
3006 txIn.PreviousOutPoint, e,
3007 )
3008 }
3009 if txDetails == nil {
3010 return fmt.Errorf(
3011 "%v not found",
3012 txIn.PreviousOutPoint,
3013 )
3014 }
3015 prevOutScript = txDetails.MsgTx.TxOut[prevIndex].PkScript
3016 }
3017 // Set up our callbacks that we pass to txscript so it can look up the
3018 // appropriate keys and scripts by address.
3019 getKey := txscript.KeyClosure(
3020 func(addr btcaddr.Address) (*ec.PrivateKey, bool, error) {
3021 if len(additionalKeysByAddress) != 0 {
3022 addrStr := addr.EncodeAddress()
3023 var wif *util.WIF
3024 wif, ok = additionalKeysByAddress[addrStr]
3025 if !ok {
3026 return nil, false,
3027 errors.New("no key for address")
3028 }
3029 return wif.PrivKey, wif.CompressPubKey, nil
3030 }
3031 var address waddrmgr.ManagedAddress
3032 address, e = w.Manager.Address(addrmgrNs, addr)
3033 if e != nil {
3034 return nil, false, e
3035 }
3036 var pka waddrmgr.ManagedPubKeyAddress
3037 pka, ok = address.(waddrmgr.ManagedPubKeyAddress)
3038 if !ok {
3039 return nil, false, fmt.Errorf(
3040 "address %v is not "+
3041 "a pubkey address", address.Address().EncodeAddress(),
3042 )
3043 }
3044 var key *ec.PrivateKey
3045 key, e = pka.PrivKey()
3046 if e != nil {
3047 return nil, false, e
3048 }
3049 return key, pka.Compressed(), nil
3050 },
3051 )
3052 getScript := txscript.ScriptClosure(
3053 func(addr btcaddr.Address) ([]byte, error) {
3054 // If keys were provided then we can only use the redeem scripts provided with
3055 // our inputs, too.
3056 if len(additionalKeysByAddress) != 0 {
3057 addrStr := addr.EncodeAddress()
3058 script, ok := p2shRedeemScriptsByAddress[addrStr]
3059 if !ok {
3060 return nil, errors.New("no script for address")
3061 }
3062 return script, nil
3063 }
3064 address, e := w.Manager.Address(addrmgrNs, addr)
3065 if e != nil {
3066 return nil, e
3067 }
3068 sa, ok := address.(waddrmgr.ManagedScriptAddress)
3069 if !ok {
3070 return nil, errors.New(
3071 "address is not a script" +
3072 " address",
3073 )
3074 }
3075 return sa.Script()
3076 },
3077 )
3078 // SigHashSingle inputs can only be signed if there's a corresponding output. However this could be already
3079 // signed, so we always verify the output.
3080 if (hashType&txscript.SigHashSingle) !=
3081 txscript.SigHashSingle || i < len(tx.TxOut) {
3082 script, e := txscript.SignTxOutput(
3083 w.ChainParams(),
3084 tx, i, prevOutScript, hashType, getKey,
3085 getScript, txIn.SignatureScript,
3086 )
3087 // Failure to sign isn't an error, it just means that the tx isn't complete.
3088 if e != nil {
3089 signErrors = append(
3090 signErrors, SignatureError{
3091 InputIndex: uint32(i),
3092 Error: e,
3093 },
3094 )
3095 continue
3096 }
3097 txIn.SignatureScript = script
3098 }
3099 // Either it was already signed or we just signed it. Find out if it is completely satisfied or still needs
3100 // more.
3101 vm, e := txscript.NewEngine(
3102 prevOutScript, tx, i,
3103 txscript.StandardVerifyFlags, nil, nil, 0,
3104 )
3105 if e == nil {
3106 e = vm.Execute()
3107 }
3108 if e != nil {
3109 signErrors = append(
3110 signErrors, SignatureError{
3111 InputIndex: uint32(i),
3112 Error: e,
3113 },
3114 )
3115 }
3116 }
3117 return nil
3118 },
3119 )
3120 return signErrors, e
3121 }
3122 3123 // PublishTransaction sends the transaction to the consensus RPC server so it can be propagated to other nodes and
3124 // eventually mined.
3125 //
3126 // This function is unstable and will be removed once syncing code is moved out of the wallet.
3127 func (w *Wallet) PublishTransaction(tx *wire.MsgTx) (e error) {
3128 _, e = w.publishTransaction(tx)
3129 return e
3130 }
3131 3132 // publishTransaction is the private version of PublishTransaction which contains the primary logic required for
3133 // publishing a transaction, updating the relevant database state, and finally possible removing the transaction from
3134 // the database (along with cleaning up all inputs used, and outputs created) if the transaction is rejected by the back
3135 // end.
3136 func (w *Wallet) publishTransaction(tx *wire.MsgTx) (*chainhash.Hash, error) {
3137 server, e := w.requireChainClient()
3138 if e != nil {
3139 return nil, e
3140 }
3141 // As we aim for this to be general reliable transaction broadcast API, we'll write this tx to disk as an
3142 // unconfirmed transaction. This way, upon restarts, we'll always rebroadcast it, and also add it to our set of
3143 // records.
3144 txRec, e := wtxmgr.NewTxRecordFromMsgTx(tx, time.Now())
3145 if e != nil {
3146 return nil, e
3147 }
3148 e = walletdb.Update(
3149 w.db, func(dbTx walletdb.ReadWriteTx) (e error) {
3150 return w.addRelevantTx(dbTx, txRec, nil)
3151 },
3152 )
3153 if e != nil {
3154 return nil, e
3155 }
3156 txid, e := server.SendRawTransaction(tx, false)
3157 switch {
3158 case e == nil:
3159 return txid, nil
3160 // The following are errors returned from pod's mempool.
3161 case strings.Contains(e.Error(), "spent"):
3162 fallthrough
3163 case strings.Contains(e.Error(), "orphan"):
3164 fallthrough
3165 case strings.Contains(e.Error(), "conflict"):
3166 fallthrough
3167 // The following errors are returned from bitcoind's mempool.
3168 case strings.Contains(e.Error(), "fee not met"):
3169 fallthrough
3170 case strings.Contains(e.Error(), "Missing inputs"):
3171 fallthrough
3172 case strings.Contains(e.Error(), "already in block chain"):
3173 // If the transaction was rejected, then we'll remove it from the txstore, as otherwise, we'll attempt to
3174 // continually re-broadcast it, and the utxo state of the wallet won't be accurate.
3175 dbErr := walletdb.Update(
3176 w.db, func(dbTx walletdb.ReadWriteTx) (e error) {
3177 txmgrNs := dbTx.ReadWriteBucket(wtxmgrNamespaceKey)
3178 return w.TxStore.RemoveUnminedTx(txmgrNs, txRec)
3179 },
3180 )
3181 if dbErr != nil {
3182 return nil, fmt.Errorf(
3183 "unable to broadcast tx: %v, "+
3184 "unable to remove invalid tx: %v", e, dbErr,
3185 )
3186 }
3187 return nil, e
3188 default:
3189 return nil, e
3190 }
3191 }
3192 3193 // ChainParams returns the network parameters for the blockchain the wallet belongs to.
3194 func (w *Wallet) ChainParams() *chaincfg.Params {
3195 return w.chainParams
3196 }
3197 3198 // Database returns the underlying walletdb database. This method is provided in order to allow applications wrapping
3199 // btcwallet to store node-specific data with the wallet's database.
3200 func (w *Wallet) Database() walletdb.DB {
3201 return w.db
3202 }
3203 3204 // Create creates an new wallet, writing it to an empty database. If the passed seed is non-nil, it is used. Otherwise,
3205 // a secure random seed of the recommended length is generated.
3206 func Create(
3207 db walletdb.DB, pubPass, privPass, seed []byte, params *chaincfg.Params,
3208 birthday time.Time,
3209 ) (e error) {
3210 // If a seed was provided, ensure that it is of valid length. Otherwise, we generate a random seed for the wallet
3211 // with the recommended seed length.
3212 if seed == nil {
3213 hdSeed, e := hdkeychain.GenerateSeed(
3214 hdkeychain.RecommendedSeedLen,
3215 )
3216 if e != nil {
3217 return e
3218 }
3219 seed = hdSeed
3220 }
3221 if len(seed) < hdkeychain.MinSeedBytes ||
3222 len(seed) > hdkeychain.MaxSeedBytes {
3223 return hdkeychain.ErrInvalidSeedLen
3224 }
3225 return walletdb.Update(
3226 db, func(tx walletdb.ReadWriteTx) (e error) {
3227 addrmgrNs, e := tx.CreateTopLevelBucket(waddrmgrNamespaceKey)
3228 if e != nil {
3229 return e
3230 }
3231 txmgrNs, e := tx.CreateTopLevelBucket(wtxmgrNamespaceKey)
3232 if e != nil {
3233 return e
3234 }
3235 e = waddrmgr.Create(
3236 addrmgrNs, seed, pubPass, privPass, params, nil,
3237 birthday,
3238 )
3239 if e != nil {
3240 return e
3241 }
3242 return wtxmgr.Create(txmgrNs)
3243 },
3244 )
3245 }
3246 3247 // Open loads an already-created wallet from the passed database and namespaces.
3248 func Open(
3249 db walletdb.DB,
3250 pubPass []byte,
3251 cbs *waddrmgr.OpenCallbacks,
3252 params *chaincfg.Params,
3253 recoveryWindow uint32,
3254 podConfig *config.Config,
3255 quit qu.C,
3256 ) (*Wallet, error) {
3257 // debug.PrintStack()
3258 W.Ln("opening wallet") // , string(pubPass))
3259 e := walletdb.View(
3260 db, func(tx walletdb.ReadTx) (e error) {
3261 waddrmgrBucket := tx.ReadBucket(waddrmgrNamespaceKey)
3262 if waddrmgrBucket == nil {
3263 return errors.New("missing address manager namespace")
3264 }
3265 wtxmgrBucket := tx.ReadBucket(wtxmgrNamespaceKey)
3266 if wtxmgrBucket == nil {
3267 return errors.New("missing transaction manager namespace")
3268 }
3269 return nil
3270 },
3271 )
3272 if e != nil {
3273 return nil, e
3274 }
3275 T.Ln("opened wallet")
3276 // Perform upgrades as necessary. Each upgrade is done under its own transaction, which is managed by each package
3277 // itself, so the entire DB is passed instead of passing already opened write transaction.
3278 //
3279 // This will need to change later when upgrades in one package depend on data in another (such as removing chain
3280 // synchronization from address manager).
3281 T.Ln("doing address manager upgrades")
3282 e = waddrmgr.DoUpgrades(db, waddrmgrNamespaceKey, pubPass, params, cbs)
3283 if e != nil {
3284 return nil, e
3285 }
3286 T.Ln("doing txmanager upgrades")
3287 e = wtxmgr.DoUpgrades(db, wtxmgrNamespaceKey)
3288 if e != nil {
3289 return nil, e
3290 }
3291 // Open database abstraction instances
3292 var (
3293 addrMgr *waddrmgr.Manager
3294 txMgr *wtxmgr.Store
3295 )
3296 T.Ln("opening wallet database abstraction instances")
3297 e = walletdb.View(
3298 db, func(tx walletdb.ReadTx) (e error) {
3299 T.Ln("reading address bucket")
3300 addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
3301 T.Ln("reading tx bucket")
3302 txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
3303 T.Ln("opening address manager")
3304 addrMgr, e = waddrmgr.Open(addrmgrNs, pubPass, params)
3305 if e != nil {
3306 E.Ln(e, "'"+string(pubPass)+"'")
3307 return e
3308 }
3309 T.Ln("opening transaction manager")
3310 txMgr, e = wtxmgr.Open(txmgrNs, params)
3311 T.Ln("wallet database abstraction instances opened")
3312 return e
3313 },
3314 )
3315 if e != nil {
3316 return nil, e
3317 }
3318 T.Ln("creating wallet state") // TODO: log balance? last sync height?
3319 w := &Wallet{
3320 publicPassphrase: pubPass,
3321 db: db,
3322 Manager: addrMgr,
3323 TxStore: txMgr,
3324 lockedOutpoints: map[wire.OutPoint]struct{}{},
3325 recoveryWindow: recoveryWindow,
3326 rescanAddJob: make(chan *RescanJob),
3327 rescanBatch: make(chan *rescanBatch),
3328 rescanNotifications: make(chan interface{}),
3329 rescanProgress: make(chan *RescanProgressMsg),
3330 rescanFinished: make(chan *RescanFinishedMsg),
3331 createTxRequests: make(chan createTxRequest),
3332 unlockRequests: make(chan unlockRequest),
3333 lockRequests: qu.T(),
3334 holdUnlockRequests: make(chan chan heldUnlock),
3335 lockState: make(chan bool),
3336 changePassphrase: make(chan changePassphraseRequest),
3337 changePassphrases: make(chan changePassphrasesRequest),
3338 chainParams: params,
3339 PodConfig: podConfig,
3340 quit: quit,
3341 }
3342 w.NtfnServer = newNotificationServer(w)
3343 w.TxStore.NotifyUnspent = func(hash *chainhash.Hash, index uint32) {
3344 w.NtfnServer.notifyUnspentOutput(0, hash, index)
3345 }
3346 T.Ln("wallet state created")
3347 return w, nil
3348 }
3349