From f310bb68258d3dd363718cccc5b26030ebce66e5 Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Tue, 25 Sep 2018 09:41:31 -0400 Subject: [PATCH 1/2] [API] fix account_unlock, account_decrypt, and account_encrypt commands --- lbrynet/daemon/Daemon.py | 46 ++++++++++++++++------------------ lbrynet/wallet/manager.py | 33 +++++++++++++++++++++--- tests/unit/core/test_Wallet.py | 34 ------------------------- 3 files changed, 50 insertions(+), 63 deletions(-) diff --git a/lbrynet/daemon/Daemon.py b/lbrynet/daemon/Daemon.py index 773e7b494..f2b49c4ab 100644 --- a/lbrynet/daemon/Daemon.py +++ b/lbrynet/daemon/Daemon.py @@ -175,7 +175,7 @@ class DHTHasContacts(RequiredCondition): return len(component.contacts) > 0 -class WalletIsLocked(RequiredCondition): +class WalletIsUnlocked(RequiredCondition): name = WALLET_IS_UNLOCKED component = WALLET_COMPONENT message = "your wallet is locked" @@ -1361,64 +1361,60 @@ class Daemon(AuthJSONRPCServer): return result @requires(WALLET_COMPONENT) - @defer.inlineCallbacks - def jsonrpc_account_unlock(self, password): + def jsonrpc_account_unlock(self, password, account_id=None): """ Unlock an encrypted account Usage: - account_unlock ( | --password=) + account_unlock ( | --password=) [ | --account_id=] Options: - --password= : (str) password for unlocking wallet + --account_id= : (str) id for the account to unlock Returns: (bool) true if account is unlocked, otherwise false """ - # the check_locked() in the if statement is needed because that is what sets - # the wallet_unlocked_d deferred ¯\_(ツ)_/¯ - if not self.wallet_manager.check_locked(): - d = self.wallet_manager.wallet_unlocked_d - d.callback(password) - result = yield d - else: - result = True - response = yield self._render_response(result) - defer.returnValue(response) + return self.wallet_manager.unlock_account( + password, self.get_account_or_default(account_id, lbc_only=False) + ) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) - def jsonrpc_account_decrypt(self): + def jsonrpc_account_decrypt(self, account_id=None): """ Decrypt an encrypted account, this will remove the wallet password Usage: - account_decrypt + account_decrypt [ | --account_id=] Options: - None + --account_id= : (str) id for the account to decrypt Returns: (bool) true if wallet is decrypted, otherwise false """ - return self.wallet_manager.decrypt_wallet() + + return self.wallet_manager.decrypt_account(self.get_account_or_default(account_id, lbc_only=False)) @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) - def jsonrpc_account_encrypt(self, new_password): + def jsonrpc_account_encrypt(self, new_password, account_id=None): """ - Encrypt a wallet with a password, if the wallet is already encrypted this will update - the password + Encrypt an unencrypted account with a password Usage: - wallet_encrypt ( | --new_password=) + wallet_encrypt ( | --new_password=) [ | --account_id=] Options: - --new_password= : (str) password string to be used for encrypting wallet + --account_id= : (str) id for the account to encrypt Returns: (bool) true if wallet is decrypted, otherwise false """ - return self.wallet_manager.encrypt_wallet(new_password) + + return self.wallet_manager.encrypt_account( + new_password, + self.get_account_or_default(account_id, lbc_only=False) + ) @requires("wallet") def jsonrpc_account_max_address_gap(self, account_id): diff --git a/lbrynet/wallet/manager.py b/lbrynet/wallet/manager.py index ce3fb2237..3c28487b3 100644 --- a/lbrynet/wallet/manager.py +++ b/lbrynet/wallet/manager.py @@ -12,6 +12,7 @@ from .account import generate_certificate from .transaction import Transaction from .database import WalletDatabase + log = logging.getLogger(__name__) @@ -54,8 +55,7 @@ class LbryWalletManager(BaseWalletManager): @property def use_encryption(self): - # TODO: implement this - return False + return self.default_account.serialize_encrypted @property def is_first_run(self): @@ -63,10 +63,35 @@ class LbryWalletManager(BaseWalletManager): @property def is_wallet_unlocked(self): - return True + return not self.default_account.encrypted def check_locked(self): - return defer.succeed(False) + return defer.succeed(self.default_account.encrypted) + + def decrypt_account(self, account): + assert account.password is not None, "account is not unlocked" + assert not account.encrypted, "account is not unlocked" + account.serialize_encrypted = False + self.save() + return not account.encrypted and not account.serialize_encrypted + + def encrypt_account(self, password, account): + assert not account.encrypted, "account is already encrypted" + account.encrypt(password) + account.serialize_encrypted = True + self.save() + return account.encrypted and account.serialize_encrypted + + def unlock_account(self, password, account): + assert account.encrypted, "account is not locked" + account.decrypt(password) + return not account.encrypted + + def lock_account(self, account): + assert account.password is not None, "account is already locked" + assert not account.encrypted and account.serialize_encrypted, "account is not encrypted" + account.encrypt(account.password) + return account.encrypted @staticmethod def migrate_lbryum_to_torba(path): diff --git a/tests/unit/core/test_Wallet.py b/tests/unit/core/test_Wallet.py index 41e0e3eb6..d5d3688d3 100644 --- a/tests/unit/core/test_Wallet.py +++ b/tests/unit/core/test_Wallet.py @@ -247,37 +247,3 @@ class WalletTest(unittest.TestCase): self.assertFailure(d, InsufficientFundsError) return d - -class WalletEncryptionTests(unittest.TestCase): - skip = "Needs to be ported to the new wallet." - - def setUp(self): - user_dir = tempfile.mkdtemp() - self.wallet = MocLbryumWallet(user_dir) - return self.wallet.setup(password="password") - - def tearDown(self): - return self.wallet.stop() - - def test_unlock_wallet(self): - self.wallet._cmd_runner = Commands( - self.wallet.config, self.wallet.wallet, self.wallet.network, None, "password") - cmd_runner = self.wallet.get_cmd_runner() - cmd_runner.unlock_wallet("password") - self.assertIsNone(cmd_runner.new_password) - self.assertEqual(cmd_runner._password, "password") - - def test_encrypt_decrypt_wallet(self): - self.wallet._cmd_runner = Commands( - self.wallet.config, self.wallet.wallet, self.wallet.network, None, "password") - self.wallet.encrypt_wallet("secret2", False) - self.wallet.decrypt_wallet() - - def test_update_password_keyring_off(self): - self.wallet.config.use_keyring = False - self.wallet._cmd_runner = Commands( - self.wallet.config, self.wallet.wallet, self.wallet.network, None, "password") - - # no keyring available, so ValueError is expected - with self.assertRaises(ValueError): - self.wallet.encrypt_wallet("secret2", True) From 66f33202c7fc1eb8964c9efb0726d5acefe2100e Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Tue, 25 Sep 2018 09:41:41 -0400 Subject: [PATCH 2/2] [API] add account_lock --- lbrynet/daemon/Daemon.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lbrynet/daemon/Daemon.py b/lbrynet/daemon/Daemon.py index f2b49c4ab..3584c1249 100644 --- a/lbrynet/daemon/Daemon.py +++ b/lbrynet/daemon/Daemon.py @@ -1379,6 +1379,23 @@ class Daemon(AuthJSONRPCServer): password, self.get_account_or_default(account_id, lbc_only=False) ) + @requires(WALLET_COMPONENT) + def jsonrpc_account_lock(self, account_id=None): + """ + Lock an unlocked account + + Usage: + account_lock [ | --account_id=] + + Options: + --account_id= : (str) id for the account to lock + + Returns: + (bool) true if account is locked, otherwise false + """ + + return self.wallet_manager.lock_account(self.get_account_or_default(account_id, lbc_only=False)) + @requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) def jsonrpc_account_decrypt(self, account_id=None): """