From b09e4f52008aa840cc7a2f957b628957a34fcc6c Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 27 Jan 2014 15:48:12 -0500 Subject: [PATCH] Lock/unlock all account wallets. Now that it has been decided that all account wallets will share the same passphrase, the walletlock and walletpassphrase RPC handlers now go through the accountstore to lock or unlock all account wallets, rather than only changing the default account. --- account.go | 22 ++++++++++++------ accountstore.go | 36 +++++++++++++++++++++++++++++ cmdmgr.go | 60 +++++++++---------------------------------------- 3 files changed, 61 insertions(+), 57 deletions(-) diff --git a/account.go b/account.go index dec21b9..83f7d52 100644 --- a/account.go +++ b/account.go @@ -122,11 +122,18 @@ func (a *Account) Lock() error { a.mtx.Lock() defer a.mtx.Unlock() - err := a.Wallet.Lock() - if err == nil { + switch err := a.Wallet.Lock(); err { + case nil: NotifyWalletLockStateChange(a.Name(), true) + return nil + + case wallet.ErrWalletLocked: + // Do not pass wallet already locked errors to the caller. + return nil + + default: + return err } - return err } // Unlock unlocks the underlying wallet for an account. @@ -134,11 +141,12 @@ func (a *Account) Unlock(passphrase []byte) error { a.mtx.Lock() defer a.mtx.Unlock() - err := a.Wallet.Unlock(passphrase) - if err == nil { - NotifyWalletLockStateChange(a.Name(), false) + if err := a.Wallet.Unlock(passphrase); err != nil { + return err } - return a.Wallet.Unlock(passphrase) + + NotifyWalletLockStateChange(a.Name(), false) + return nil } // Rollback reverts each stored Account to a state before the block diff --git a/accountstore.go b/accountstore.go index d7b4a72..f620023 100644 --- a/accountstore.go +++ b/accountstore.go @@ -295,6 +295,42 @@ func (store *AccountStore) ChangePassphrase(old, new []byte) error { return nil } +// LockWallets locks all account's wallets in the store. +func (store *AccountStore) LockWallets() error { + store.RLock() + defer store.RUnlock() + + for _, a := range store.accounts { + if err := a.Lock(); err != nil { + return err + } + } + + return nil +} + +// UnlockWallets unlocks all account's wallets in the store with the provided +// passphrase. If any wallet unlocks fail, all successfully unlocked wallets +// are locked again. +func (store *AccountStore) UnlockWallets(passphrase string) error { + store.RLock() + defer store.RUnlock() + + unlockedAccts := make([]*Account, 0, len(store.accounts)) + for _, a := range store.accounts { + if err := a.Unlock([]byte(passphrase)); err != nil { + for _, ua := range unlockedAccts { + ua.Lock() + } + return fmt.Errorf("cannot unlock account %v: %v", + a.name, err) + } + unlockedAccts = append(unlockedAccts, a) + } + + return nil +} + // DumpKeys returns all WIF-encoded private keys associated with all // accounts. All wallets must be unlocked for this operation to succeed. func (store *AccountStore) DumpKeys() ([]string, error) { diff --git a/cmdmgr.go b/cmdmgr.go index 7579d07..8c6e081 100644 --- a/cmdmgr.go +++ b/cmdmgr.go @@ -1213,26 +1213,11 @@ func WalletIsLocked(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return locked, nil } -// WalletLock handles a walletlock request by locking the wallet, -// returning an error if the wallet is already locked. -// -// TODO(jrick): figure out how multiple wallets/accounts will work -// with this. Lock all the wallets, like if all accounts are locked -// for one bitcoind wallet? +// WalletLock handles a walletlock request by locking the all account +// wallets, returning an error if any wallet is not encrypted (for example, +// a watching-only wallet). func WalletLock(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { - a, err := accountstore.Account("") - switch err { - case nil: - break - - case ErrAcctNotExist: - e := btcjson.Error{ - Code: btcjson.ErrWallet.Code, - Message: "default account does not exist", - } - return nil, &e - - default: // all other non-nil errors + if err := accountstore.LockWallets(); err != nil { e := btcjson.Error{ Code: btcjson.ErrWallet.Code, Message: err.Error(), @@ -1240,17 +1225,12 @@ func WalletLock(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return nil, &e } - if err := a.Lock(); err != nil { - return nil, &btcjson.ErrWalletWrongEncState - } return nil, nil } // WalletPassphrase responds to the walletpassphrase request by unlocking // the wallet. The decryption key is saved in the wallet until timeout // seconds expires, after which the wallet is locked. -// -// TODO(jrick): figure out how to do this for non-default accounts. func WalletPassphrase(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { // Type assert icmd to access parameters. cmd, ok := icmd.(*btcjson.WalletPassphraseCmd) @@ -1258,19 +1238,7 @@ func WalletPassphrase(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return nil, &btcjson.ErrInternal } - a, err := accountstore.Account("") - switch err { - case nil: - break - - case ErrAcctNotExist: - e := btcjson.Error{ - Code: btcjson.ErrWallet.Code, - Message: "default account does not exist", - } - return nil, &e - - default: // all other non-nil errors + if err := accountstore.UnlockWallets(cmd.Passphrase); err != nil { e := btcjson.Error{ Code: btcjson.ErrWallet.Code, Message: err.Error(), @@ -1278,20 +1246,12 @@ func WalletPassphrase(icmd btcjson.Cmd) (interface{}, *btcjson.Error) { return nil, &e } - switch err := a.Unlock([]byte(cmd.Passphrase)); err { - case nil: - go func(timeout int64) { - time.Sleep(time.Second * time.Duration(timeout)) - _ = a.Lock() - }(cmd.Timeout) - return nil, nil + go func(timeout int64) { + time.Sleep(time.Second * time.Duration(timeout)) + _ = accountstore.LockWallets() + }(cmd.Timeout) - case ErrAcctNotExist: - return nil, &btcjson.ErrWalletInvalidAccountName - - default: // all other non-nil errors - return nil, &btcjson.ErrWalletPassphraseIncorrect - } + return nil, nil } // WalletPassphraseChange responds to the walletpassphrasechange request