Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix address pool panics on start up #143

Merged
merged 2 commits into from
Apr 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions dcrwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package main

import (
"bufio"
"io/ioutil"
"net"
"net/http"
Expand All @@ -16,7 +17,10 @@ import (
"sync"
"time"

"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrwallet/chain"
"github.com/decred/dcrwallet/internal/prompt"
"github.com/decred/dcrwallet/internal/zero"
"github.com/decred/dcrwallet/rpc/legacyrpc"
"github.com/decred/dcrwallet/wallet"
)
Expand Down Expand Up @@ -112,6 +116,7 @@ func walletMain() error {
}

loader.RunAfterLoad(func(w *wallet.Wallet) {
startPromptPass(w)
startWalletRPCServices(w, rpcs, legacyRPCServer)
})

Expand Down Expand Up @@ -160,6 +165,66 @@ func walletMain() error {
return nil
}

// startPromptPass prompts the user for a password to unlock their wallet in
// the event that it was restored from seed or --promptpass flag is set.
func startPromptPass(w *wallet.Wallet) {
promptPass := cfg.PromptPass

// The wallet is totally desynced, so we need to resync accounts.
// Prompt for the password. Then, set the flag it wallet so it
// knows which address functions to call when resyncing.
firstRunBS := w.Manager.SyncedTo()
if firstRunBS.Hash == *w.ChainParams().GenesisHash {
promptPass = true
}
if promptPass {
w.SetResyncAccounts(true)
log.Infof("Please enter the private wallet passphrase. " +
"This will complete syncing of the wallet accounts " +
"and then leave your wallet unlocked. You may relock " +
"wallet after by calling 'walletlock' through the RPC.")
} else {
return
}

// We need to rescan accounts for the initial sync. Unlock the
// wallet after prompting for the passphrase. The special case
// of a --createtemp simnet wallet is handled by first
// attempting to automatically open it with the default
// passphrase. The wallet should also request to be unlocked
// if stake mining is currently on, so users with this flag
// are prompted here as well.
for {
if w.ChainParams() == &chaincfg.SimNetParams {
var unlockAfter <-chan time.Time
err := w.Unlock(wallet.SimulationPassphrase, unlockAfter)
if err == nil {
// Unlock success with the default password.
return
}
}
if promptPass {
reader := bufio.NewReader(os.Stdin)
passphrase, err := prompt.PromptPass(reader, "", false)
if err != nil {
log.Errorf("Failed to input password. Please try again.")
continue
}
defer zero.Bytes(passphrase)

var unlockAfter <-chan time.Time
err = w.Unlock(passphrase, unlockAfter)
if err != nil {
log.Errorf("Incorrect password entered. Please " +
"try again.")
continue
}

break
}
}
}

// rpcClientConnectLoop continuously attempts a connection to the consensus RPC
// server. When a connection is established, the client is used to sync the
// loaded wallet, either immediately or when loaded at a later time.
Expand Down
48 changes: 48 additions & 0 deletions wallet/addresspool.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,42 @@ func (w *Wallet) CloseAddressPools() {
return
}

// CheckAddressPoolsInitialized checks to make sure an address pool exists
// that that one can safely access functions and internal memory such as
// mutexes.
func (w *Wallet) CheckAddressPoolsInitialized(account uint32) error {
if w.addrPools[account] == nil {
return fmt.Errorf("Address pools for account %v "+
"are undeclared", account)
}
if w.addrPools[account].external == nil {
return fmt.Errorf("External address pool for "+
"account %v is undeclared", account)
}
if w.addrPools[account].internal == nil {
return fmt.Errorf("Internal address pool for "+
"account %v is undeclared", account)
}
if !w.addrPools[account].external.started {
return fmt.Errorf("External address pool for the "+
"account %v is uninitialized", account)
}
if !w.addrPools[account].internal.started {
return fmt.Errorf("Internal address pool for the "+
"account %v is uninitialized", account)
}

return nil
}

// GetNewAddressExternal is the exported function that gets a new external address
// for the default account from the external address mempool.
func (w *Wallet) GetNewAddressExternal() (dcrutil.Address, error) {
err := w.CheckAddressPoolsInitialized(waddrmgr.DefaultAccountNum)
if err != nil {
return nil, err
}

w.addrPools[waddrmgr.DefaultAccountNum].external.mutex.Lock()
defer w.addrPools[waddrmgr.DefaultAccountNum].external.mutex.Unlock()
address, err :=
Expand All @@ -283,6 +316,11 @@ func (w *Wallet) GetNewAddressExternal() (dcrutil.Address, error) {
// GetNewAddressInternal is the exported function that gets a new internal address
// for the default account from the internal address mempool.
func (w *Wallet) GetNewAddressInternal() (dcrutil.Address, error) {
err := w.CheckAddressPoolsInitialized(waddrmgr.DefaultAccountNum)
if err != nil {
return nil, err
}

w.addrPools[waddrmgr.DefaultAccountNum].internal.mutex.Lock()
defer w.addrPools[waddrmgr.DefaultAccountNum].internal.mutex.Unlock()
address, err :=
Expand All @@ -296,6 +334,11 @@ func (w *Wallet) GetNewAddressInternal() (dcrutil.Address, error) {
// NewAddress returns the next external chained address for a wallet given some
// account.
func (w *Wallet) NewAddress(account uint32) (dcrutil.Address, error) {
err := w.CheckAddressPoolsInitialized(account)
if err != nil {
return nil, err
}

// Get next address from wallet.
addr, err := w.addrPools[account].external.GetNewAddress()
if err != nil {
Expand Down Expand Up @@ -328,6 +371,11 @@ func (w *Wallet) NewAddress(account uint32) (dcrutil.Address, error) {

// NewChangeAddress returns a new change address for a wallet.
func (w *Wallet) NewChangeAddress(account uint32) (dcrutil.Address, error) {
err := w.CheckAddressPoolsInitialized(account)
if err != nil {
return nil, err
}

// Get next chained change address from wallet for account.
addr, err := w.addrPools[account].internal.GetNewAddress()
if err != nil {
Expand Down
Loading