wallet: remove sync.go, references decred
This commit is contained in:
parent
bf86ccf5b4
commit
14e6fe36d0
1 changed files with 0 additions and 710 deletions
710
wallet/sync.go
710
wallet/sync.go
|
@ -1,710 +0,0 @@
|
|||
//+build ignore
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013-2016 The btcsuite developers
|
||||
* Copyright (c) 2015 The Decred developers
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package wallet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
|
||||
"github.com/decred/bitset"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrwallet/chain"
|
||||
"github.com/decred/dcrwallet/waddrmgr"
|
||||
"github.com/decred/dcrwallet/walletdb"
|
||||
)
|
||||
|
||||
// finalScanLength is the final length of accounts to scan for the
|
||||
// function below.
|
||||
var finalAcctScanLength = 50
|
||||
|
||||
// acctSeekWidth is the number of addresses for both internal and external
|
||||
// branches to scan to determine whether or not an account exists and should
|
||||
// be rescanned. This is the tolerance for account gaps as well.
|
||||
var acctSeekWidth uint32 = 5
|
||||
|
||||
// accountIsUsed checks if an account has ever been used by scanning the
|
||||
// first acctSeekWidth many addresses for usage.
|
||||
func (w *Wallet) accountIsUsed(account uint32, chainClient *chain.RPCClient) bool {
|
||||
// Search external branch then internal branch for a used
|
||||
// address. We need to set the address function to use based
|
||||
// on whether or not this is the initial sync. The function
|
||||
// AddressDerivedFromCointype is able to see addresses that
|
||||
// exists in accounts that have not yet been created, while
|
||||
// AddressDerivedFromDbAcct can not.
|
||||
addrFunc := w.Manager.AddressDerivedFromDbAcct
|
||||
if w.initiallyUnlocked {
|
||||
addrFunc = w.Manager.AddressDerivedFromCointype
|
||||
}
|
||||
|
||||
for branch := uint32(0); branch < 2; branch++ {
|
||||
for i := uint32(0); i < acctSeekWidth; i++ {
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = addrFunc(addrmgrNs, i, account, branch)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
// Skip erroneous keys, which happen rarely.
|
||||
continue
|
||||
}
|
||||
|
||||
exists, err := chainClient.ExistsAddress(addr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if exists {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// bisectLastAcctIndex is a helper function for searching through accounts to
|
||||
// find the last used account. It uses logarithmic scanning to determine if
|
||||
// an account has been used.
|
||||
func (w *Wallet) bisectLastAcctIndex(hi, low int) int {
|
||||
chainClient, err := w.requireChainClient()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
offset := low
|
||||
for i := hi - low - 1; i > 0; i /= 2 {
|
||||
if i+offset+int(acctSeekWidth) < waddrmgr.MaxAddressesPerAccount {
|
||||
for j := i + offset + int(addrSeekWidth); j >= i+offset; j-- {
|
||||
if w.accountIsUsed(uint32(j), chainClient) {
|
||||
return i + offset
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if w.accountIsUsed(uint32(i+offset), chainClient) {
|
||||
return i + offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// findAcctEnd is a helper function for searching for the last used account by
|
||||
// logarithmic scanning of the account indexes.
|
||||
func (w *Wallet) findAcctEnd(start, stop int) int {
|
||||
indexStart := w.bisectLastAcctIndex(stop, start)
|
||||
indexLast := 0
|
||||
for {
|
||||
indexLastStored := indexStart
|
||||
low := indexLastStored
|
||||
hi := indexLast + ((indexStart - indexLast) * 2) + 1
|
||||
indexStart = w.bisectLastAcctIndex(hi, low)
|
||||
indexLast = indexLastStored
|
||||
|
||||
if indexStart == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return indexLast
|
||||
}
|
||||
|
||||
// scanAccountIndex identifies the last used address in an HD keychain of public
|
||||
// keys. It returns the index of the last used key, along with the address of
|
||||
// this key.
|
||||
func (w *Wallet) scanAccountIndex(start int, end int) (uint32, error) {
|
||||
chainClient, err := w.requireChainClient()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Find the last used account. Scan from it to the end in case there was a
|
||||
// gap from that position, which is possible. Then, return the account
|
||||
// in that position.
|
||||
lastUsed := w.findAcctEnd(start, end)
|
||||
if lastUsed != 0 {
|
||||
for i := lastUsed + finalAcctScanLength; i >= lastUsed; i-- {
|
||||
if w.accountIsUsed(uint32(i), chainClient) {
|
||||
return uint32(i), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can't find any used addresses. The account is
|
||||
// unused.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// debugScanLength is the final length of keys to scan past the
|
||||
// last index returned from the logarithmic scanning function
|
||||
// when creating the debug string of used addresses.
|
||||
var debugAddrScanLength = 3500
|
||||
|
||||
// addrSeekWidth is the number of new addresses to generate and add to the
|
||||
// address manager when trying to sync up a wallet to the main chain. This
|
||||
// is the maximum gap introduced by a resyncing as well, and should be less
|
||||
// than finalScanLength above.
|
||||
// TODO Optimize the scanning so that rather than overshooting the end address,
|
||||
// you instead step through addresses incrementally until reaching idx so that
|
||||
// you don't reach a gap. This can be done by keeping track of where the current
|
||||
// cursor is and adding addresses in big chunks until you hit the end.
|
||||
var addrSeekWidth uint32 = 20
|
||||
|
||||
// errDerivation is an error type signifying that the waddrmgr failed to
|
||||
// derive a key.
|
||||
var errDerivation = fmt.Errorf("failed to derive key")
|
||||
|
||||
// scanAddressRange scans backwards from end to start many addresses in the
|
||||
// account branch, and return the first index that is found on the blockchain.
|
||||
// If the address doesn't exist, false is returned as the first argument.
|
||||
func (w *Wallet) scanAddressRange(account uint32, branch uint32, start int,
|
||||
end int, chainClient *chain.RPCClient) (bool, int, error) {
|
||||
|
||||
var addresses []dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addresses, err = w.Manager.AddressesDerivedFromDbAcct(addrmgrNs,
|
||||
uint32(start), uint32(end+1), account, branch)
|
||||
if err != nil {
|
||||
return errDerivation
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
// Whether or not the addresses exist is encoded as a binary
|
||||
// bitset.
|
||||
exists, err := chainClient.ExistsAddresses(addresses)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
existsB, err := hex.DecodeString(exists)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
set := bitset.Bytes(existsB)
|
||||
|
||||
// Prevent a panic when an empty message is passed as a response.
|
||||
if len(set) == 0 {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
// Scan backwards and return if we find an address exists.
|
||||
idx := end
|
||||
itr := len(addresses) - 1
|
||||
for idx >= start {
|
||||
// If the address exists in the mempool or blockchain according
|
||||
// to the bit set returned, return this index.
|
||||
if set.Get(itr) {
|
||||
return true, idx, nil
|
||||
}
|
||||
|
||||
itr--
|
||||
idx--
|
||||
}
|
||||
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
// bisectLastAddrIndex is a helper function for search through addresses.
|
||||
func (w *Wallet) bisectLastAddrIndex(hi, low int, account uint32, branch uint32) int {
|
||||
chainClient, err := w.requireChainClient()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Logarithmically scan address indexes to find the last used
|
||||
// address index. Each time the algorithm receives an end point,
|
||||
// scans a chunk of addresses at the end point, and if no
|
||||
// addresses are found, divides the address index by two and
|
||||
// repeats until it finds the last used index.
|
||||
offset := low
|
||||
for i := hi - low - 1; i > 0; i /= 2 {
|
||||
if i+offset+int(addrSeekWidth) < waddrmgr.MaxAddressesPerAccount {
|
||||
start := i + offset
|
||||
end := i + offset + int(addrSeekWidth)
|
||||
exists, idx, err := w.scanAddressRange(account, branch, start, end,
|
||||
chainClient)
|
||||
// Skip erroneous keys, which happen rarely. Don't skip
|
||||
// other errors.
|
||||
if err == errDerivation {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("unexpected error encountered during bisection "+
|
||||
"scan of account %v, branch %v: %s", account, branch,
|
||||
err.Error())
|
||||
return 0
|
||||
}
|
||||
if exists {
|
||||
return idx
|
||||
}
|
||||
} else {
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
|
||||
uint32(i+offset), account, branch)
|
||||
return err
|
||||
})
|
||||
// Skip erroneous keys, which happen rarely.
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
exists, err := chainClient.ExistsAddress(addr)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
if exists {
|
||||
return i + offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// findEnd is a helper function for searching for used addresses.
|
||||
func (w *Wallet) findAddrEnd(start, stop int, account uint32, branch uint32) int {
|
||||
indexStart := w.bisectLastAddrIndex(stop, start, account, branch)
|
||||
indexLast := 0
|
||||
for {
|
||||
indexLastStored := indexStart
|
||||
low := indexLastStored
|
||||
hi := indexLast + ((indexStart - indexLast) * 2) + 1
|
||||
indexStart = w.bisectLastAddrIndex(hi, low, account, branch)
|
||||
indexLast = indexLastStored
|
||||
|
||||
if indexStart == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return indexLast
|
||||
}
|
||||
|
||||
// debugAccountAddrGapsString is a debug function that prints a graphical outlook
|
||||
// of address usage to a string, from the perspective of the daemon.
|
||||
func debugAccountAddrGapsString(scanBackFrom int, account uint32, branch uint32,
|
||||
w *Wallet) (string, error) {
|
||||
|
||||
chainClient, err := w.requireChainClient()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
str := fmt.Sprintf("Begin debug address scan scanning backwards from "+
|
||||
"idx %v, account %v, branch %v\n", scanBackFrom, account, branch)
|
||||
buf.WriteString(str)
|
||||
firstUsedIndex := 0
|
||||
for i := scanBackFrom; i > 0; i-- {
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
|
||||
uint32(i), account, branch)
|
||||
return err
|
||||
})
|
||||
// Skip erroneous keys.
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
exists, err := chainClient.ExistsAddress(addr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to access chain server: %v",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
if exists {
|
||||
firstUsedIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
str = fmt.Sprintf("Last used index found: %v\n", firstUsedIndex)
|
||||
buf.WriteString(str)
|
||||
|
||||
batchSize := 50
|
||||
batches := (firstUsedIndex / batchSize) + 1
|
||||
lastBatchSize := 0
|
||||
if firstUsedIndex%batchSize != 0 {
|
||||
lastBatchSize = firstUsedIndex - ((batches - 1) * batchSize)
|
||||
}
|
||||
|
||||
for i := 0; i < batches; i++ {
|
||||
str = fmt.Sprintf("%8v", i*batchSize)
|
||||
buf.WriteString(str)
|
||||
|
||||
start := i * batchSize
|
||||
end := (i + 1) * batchSize
|
||||
if i == batches-1 {
|
||||
// Nothing to do because last batch empty.
|
||||
if lastBatchSize == 0 {
|
||||
break
|
||||
}
|
||||
end = (i*batchSize + lastBatchSize) + 1
|
||||
}
|
||||
|
||||
for j := start; j < end; j++ {
|
||||
if j%10 == 0 {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
|
||||
char := "_"
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
|
||||
uint32(j), account, branch)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
char = "X"
|
||||
}
|
||||
|
||||
exists, err := chainClient.ExistsAddress(addr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to access chain server: %v",
|
||||
err.Error())
|
||||
}
|
||||
if exists {
|
||||
char = "#"
|
||||
}
|
||||
|
||||
buf.WriteString(char)
|
||||
}
|
||||
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// scanAddressIndex identifies the last used address in an HD keychain of public
|
||||
// keys. It returns the index of the last used key, along with the address of
|
||||
// this key.
|
||||
func (w *Wallet) scanAddressIndex(start int, end int, account uint32,
|
||||
branch uint32) (uint32, dcrutil.Address, error) {
|
||||
chainClient, err := w.requireChainClient()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Find the last used address. Scan from it to the end in case there was a
|
||||
// gap from that position, which is possible. Then, return the address
|
||||
// in that position.
|
||||
lastUsed := w.findAddrEnd(start, end, account, branch)
|
||||
|
||||
// If debug is on, do an exhaustive check and a graphical printout
|
||||
// of what the used addresses currently look like.
|
||||
if log.Level() == btclog.DebugLvl || log.Level() == btclog.TraceLvl {
|
||||
dbgStr, err := debugAccountAddrGapsString(lastUsed+debugAddrScanLength,
|
||||
account, branch, w)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to debug address gaps for account %v, "+
|
||||
"branch %v: %v", account, branch, err)
|
||||
} else {
|
||||
log.Debugf("%v", dbgStr)
|
||||
}
|
||||
}
|
||||
|
||||
// If there was a last used index, do an exhaustive final scan that
|
||||
// reexamines the last used addresses and ensures that the final index
|
||||
// we have found is correct.
|
||||
if lastUsed != 0 {
|
||||
start := lastUsed
|
||||
end := lastUsed + w.addrIdxScanLen
|
||||
exists, idx, err := w.scanAddressRange(account, branch, start, end,
|
||||
chainClient)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
lastUsed = idx
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs,
|
||||
uint32(lastUsed), account, branch)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uint32(lastUsed), addr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// In the case that 0 was returned as the last used address,
|
||||
// make sure the the 0th address was not used. If it was,
|
||||
// return this address to let the caller know that this
|
||||
// 0th address was used.
|
||||
if lastUsed == 0 {
|
||||
var addr dcrutil.Address
|
||||
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
addr, err = w.Manager.AddressDerivedFromDbAcct(addrmgrNs, 0,
|
||||
account, branch)
|
||||
return err
|
||||
})
|
||||
// Skip erroneous keys.
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
exists, err := chainClient.ExistsAddress(addr)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to access chain server: %v",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
if exists {
|
||||
return 0, addr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We can't find any used addresses for this account's
|
||||
// branch.
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// rescanActiveAddresses accesses the daemon to discover all the addresses that
|
||||
// have been used by an HD keychain stemming from this wallet in the default
|
||||
// account.
|
||||
func (w *Wallet) rescanActiveAddresses() error {
|
||||
log.Infof("Beginning a rescan of active addresses using the daemon. " +
|
||||
"This may take a while.")
|
||||
|
||||
// Start by rescanning the accounts and determining what the
|
||||
// current account index is. This scan should only ever be
|
||||
// performed if we're restoring our wallet from seed.
|
||||
lastAcct := uint32(0)
|
||||
var err error
|
||||
if w.initiallyUnlocked {
|
||||
min := 0
|
||||
max := waddrmgr.MaxAccountNum
|
||||
lastAcct, err = w.scanAccountIndex(min, max)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
lastAcctMgr, err := w.Manager.LastAccount(addrmgrNs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The address manager is not synced (wallet has been restored
|
||||
// from seed?). In this case, spawn the accounts in the address
|
||||
// manager first. The accounts are named by their respective
|
||||
// index number, as strings.
|
||||
if lastAcctMgr < lastAcct {
|
||||
for i := lastAcctMgr + 1; i <= lastAcct; i++ {
|
||||
_, err := w.Manager.NewAccount(
|
||||
addrmgrNs, strconv.Itoa(int(i)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The account manager has a greater index than the rescan.
|
||||
// It is likely that the end user created a new account but
|
||||
// did not use it yet. Rescan it anyway so that the address
|
||||
// pool is created.
|
||||
if lastAcctMgr > lastAcct {
|
||||
lastAcct = lastAcctMgr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("The last used account was %v. Beginning a rescan for "+
|
||||
"all active addresses in known accounts.", lastAcct)
|
||||
|
||||
// Rescan addresses for the both the internal and external
|
||||
// branches of the account. Insert a new address pool for
|
||||
// the respective account and initialize it.
|
||||
for acct := uint32(0); acct <= lastAcct; acct++ {
|
||||
var extIdx, intIdx uint32
|
||||
min := 0
|
||||
max := waddrmgr.MaxAddressesPerAccount
|
||||
|
||||
// Do this for both external (0) and internal (1) branches.
|
||||
for branch := uint32(0); branch < 2; branch++ {
|
||||
idx, lastAddr, err := w.scanAddressIndex(min, max, acct, branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
|
||||
// If the account is unused, buffer the initial address pool
|
||||
// by syncing the address manager upstream.
|
||||
unusedAcct := (lastAddr == nil)
|
||||
if unusedAcct {
|
||||
_, err := w.Manager.SyncAccountToAddrIndex(
|
||||
addrmgrNs, acct, addressPoolBuffer, branch)
|
||||
if err != nil {
|
||||
// A ErrSyncToIndex error indicates that we're already
|
||||
// synced to beyond the end of the account in the
|
||||
// waddrmgr.
|
||||
errWaddrmgr, ok := err.(waddrmgr.ManagerError)
|
||||
if !ok || errWaddrmgr.ErrorCode != waddrmgr.ErrSyncToIndex {
|
||||
return fmt.Errorf("failed to create initial waddrmgr "+
|
||||
"address buffer for the address pool, "+
|
||||
"account %v, branch %v: %s", acct, branch,
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branchString := "external"
|
||||
if branch == waddrmgr.InternalBranch {
|
||||
branchString = "internal"
|
||||
}
|
||||
|
||||
// Fetch the address pool index for this account and
|
||||
// branch from the database meta bucket.
|
||||
isInternal := branch == waddrmgr.InternalBranch
|
||||
oldIdx, err := w.Manager.NextToUseAddrPoolIndex(
|
||||
addrmgrNs, isInternal, acct)
|
||||
unexpectedError := false
|
||||
if err != nil {
|
||||
mErr, ok := err.(waddrmgr.ManagerError)
|
||||
if !ok {
|
||||
unexpectedError = true
|
||||
} else {
|
||||
// Skip errors where the account's address index
|
||||
// has not been store. For this case, oldIdx will
|
||||
// be the special case 0 which will always be
|
||||
// skipped in the initialization step below.
|
||||
if mErr.ErrorCode != waddrmgr.ErrMetaPoolIdxNoExist {
|
||||
unexpectedError = true
|
||||
}
|
||||
}
|
||||
if unexpectedError {
|
||||
return fmt.Errorf("got unexpected error trying to "+
|
||||
"retrieve last known addr index for acct %v, "+
|
||||
"%s branch: %v", acct, branchString, err)
|
||||
}
|
||||
}
|
||||
|
||||
// If the stored index is further along than the sync-to
|
||||
// index determined by the contents of daemon's addrindex,
|
||||
// use it to initialize the address pool instead.
|
||||
nextToUseIdx := idx
|
||||
if !unusedAcct {
|
||||
nextToUseIdx++
|
||||
}
|
||||
if oldIdx > nextToUseIdx {
|
||||
nextToUseIdx = oldIdx
|
||||
}
|
||||
nextToUseAddr, err := w.Manager.AddressDerivedFromDbAcct(
|
||||
addrmgrNs, nextToUseIdx, acct, branch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to derive next address for "+
|
||||
"account %v, branch %v: %s", acct, branch,
|
||||
err.Error())
|
||||
}
|
||||
|
||||
// Save these for the address pool startup later.
|
||||
if isInternal {
|
||||
intIdx = nextToUseIdx
|
||||
} else {
|
||||
extIdx = nextToUseIdx
|
||||
}
|
||||
|
||||
// Synchronize the account manager to our address index plus
|
||||
// an extra chunk of addresses that are used as a buffer
|
||||
// in the address pool.
|
||||
_, err = w.Manager.SyncAccountToAddrIndex(addrmgrNs,
|
||||
acct, nextToUseIdx+addressPoolBuffer, branch)
|
||||
if err != nil {
|
||||
// A ErrSyncToIndex error indicates that we're already
|
||||
// synced to beyond the end of the account in the
|
||||
// waddrmgr.
|
||||
errWaddrmgr, ok := err.(waddrmgr.ManagerError)
|
||||
if !ok || errWaddrmgr.ErrorCode != waddrmgr.ErrSyncToIndex {
|
||||
return fmt.Errorf("couldn't sync %s addresses in "+
|
||||
"address manager: %v", branchString, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Set the next address in the waddrmgr database so that the
|
||||
// address pool can synchronize properly after.
|
||||
err = w.Manager.StoreNextToUseAddress(
|
||||
addrmgrNs, isInternal, acct, nextToUseIdx)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to store next to use pool idx for "+
|
||||
"%s pool in the manager on init sync: %v",
|
||||
branchString, err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Successfully synchronized the address manager to "+
|
||||
"%s address %v (key index %v) for account %v",
|
||||
branchString,
|
||||
nextToUseAddr.String(),
|
||||
nextToUseIdx,
|
||||
acct)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pool, err := newAddressPools(acct, intIdx, extIdx, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.addrPoolsMtx.Lock()
|
||||
w.addrPools[acct] = pool
|
||||
w.addrPoolsMtx.Unlock()
|
||||
}
|
||||
|
||||
log.Infof("Successfully synchronized wallet accounts to account "+
|
||||
"number %v.", lastAcct)
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue