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