From d2ad3e7ff186b756ea3ef01ab3e028487aa6cc76 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Tue, 3 Sep 2019 09:51:41 -0400 Subject: [PATCH] initial kv store --- lbry/lbry/extras/daemon/Daemon.py | 46 +++++++++++++++++++++++++++++ lbry/lbry/wallet/account.py | 9 +++++- lbry/tests/integration/test_sync.py | 28 ++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/lbry/lbry/extras/daemon/Daemon.py b/lbry/lbry/extras/daemon/Daemon.py index a52950a47..f8623656d 100644 --- a/lbry/lbry/extras/daemon/Daemon.py +++ b/lbry/lbry/extras/daemon/Daemon.py @@ -1004,6 +1004,52 @@ class Daemon(metaclass=JSONRPCServerType): setattr(c, key, cleaned) return {key: cleaned} + PREFERENCE_DOC = """ + Preferences management. + """ + + def jsonrpc_preference_get(self, key=None, account_id=None): + """ + Get preference value for key or all values if not key is passed in. + + Usage: + preference_get [] + + Options: + --key= : (str) key associated with value + --account_id= : (str) id of the account containing value + + Returns: + (dict) Dictionary of preference(s) + """ + account = self.get_account_or_default(account_id) + if key: + if key in account.preferences: + return {key: account.preferences[key]} + return + return account.preferences + + def jsonrpc_preference_set(self, key, value, account_id=None): + """ + Set preferences + + Usage: + preference_set () () + + Options: + --key= : (str) key associated with value + --value= : (str) key associated with value + --account_id= : (str) id of the account containing value + + Returns: + (dict) Dictionary with key/value of new preference + """ + account = self.get_account_or_default(account_id) + account.preferences[key] = value + account.modified_on = time.time() + self.default_wallet.save() + return {key: value} + ACCOUNT_DOC = """ Create, modify and inspect wallet accounts. """ diff --git a/lbry/lbry/wallet/account.py b/lbry/lbry/wallet/account.py index 145df6bee..9cd9ab0d1 100644 --- a/lbry/lbry/wallet/account.py +++ b/lbry/lbry/wallet/account.py @@ -27,6 +27,7 @@ class Account(BaseAccount): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.channel_keys = {} + self.preferences = {} @property def hash(self) -> bytes: @@ -38,6 +39,7 @@ class Account(BaseAccount): def apply(self, d: dict): super().apply(d) self.channel_keys.update(d.get('certificates', {})) + self.preferences.update(d.get('preferences', {})) def add_channel_private_key(self, private_key): public_key_bytes = private_key.get_verifying_key().to_der() @@ -118,17 +120,22 @@ class Account(BaseAccount): def from_dict(cls, ledger, wallet, d: dict) -> 'Account': account = super().from_dict(ledger, wallet, d) account.channel_keys = d.get('certificates', {}) + account.preferences = d.get('preferences', {}) return account - def to_dict(self, include_channel_keys=True): + def to_dict(self, include_channel_keys=True, include_preferences=True): d = super().to_dict() if include_channel_keys: d['certificates'] = self.channel_keys + if include_preferences and self.preferences: + d['preferences'] = self.preferences return d async def get_details(self, **kwargs): details = await super().get_details(**kwargs) details['certificates'] = len(self.channel_keys) + if self.preferences: + details['preferences'] = self.preferences return details def get_transaction_history(self, **constraints): diff --git a/lbry/tests/integration/test_sync.py b/lbry/tests/integration/test_sync.py index ddc240783..cae210dcf 100644 --- a/lbry/tests/integration/test_sync.py +++ b/lbry/tests/integration/test_sync.py @@ -84,3 +84,31 @@ class AccountSynchronization(AsyncioTestCase): self.daemon.jsonrpc_sync_apply('password', data=add_cert) self.assertEqual(self.daemon.jsonrpc_sync_hash(), hash_w_cert) self.assertEqual(self.account.channel_keys, {'abcdefg1234:0': '---PRIVATE KEY---'}) + + @mock.patch('time.time', mock.Mock(return_value=12345)) + def test_account_preferences_syncing(self): + starting_hash = '69afcd60a300f47933917d77ef011beeeb4decfafebbda91c144c84282c6814f' + self.account.modified_on = 123.456 + self.assertEqual(self.daemon.jsonrpc_sync_hash(), starting_hash) + self.assertEqual(self.daemon.jsonrpc_sync_apply('password')['hash'], starting_hash) + self.assertFalse(self.daemon.jsonrpc_preference_get()) + + hash_w_pref = '2fe43f0b2f8bbf1fbb55537f862d8bcb0823791019a7151c848bd5f5bd32d336' + add_pref = ( + 'czo4MTkyOjE2OjE6Jgn3nAGrfYP2usMA4KQ/73+YHAwMyiGdSWuxCmgZKlpwSpnfQv8R7R0tum/n2oTSBQxjdL' + 'OlTW+tv/G5L2GfQ5op3xaT89gN+F/JJnvf3cdWvYH7Nc+uTUMb7cKhJP7hQvFW5bb1Y3jX3EBBY00Jkqyj9RCR' + 'XPtbLVu71KbVRvCAR/oAnMEsgD+ITsC3WkXMwE3BS2LjJDQmeqbH4YXNdcjJN/JzQ6fxOmr3Uk1GqnpuhFsta8' + 'H14ViRilq1pLKOSZIN80rrm5cKq45nFO5kFeoqBCEaal4u2/OkX9nOnpQlO3E95wD8hkCmZ3i20aSte6nqwqXx' + 'ZKVRZqR2a0TjwVWB8kPXPA2ewKvPILaj190bXPl8EVu+TAnTCQwMgytinYjtcKNZmMz3ENJyI2mCANwpWlX7xl' + 'y/J+qLi5b9N+agghTxggs5rVJ/hkaue7GS542dXDrwMrw9nwGqNw3dS/lcU+1wRUQ0fnHwb/85XbbwyO2aDj2i' + 'DFNkdyLyUIiIUvB1JfWAnWqX3vQcL1REK1ePgUei7dCHJ3WyWdsRx3cVXzlK8yOPkf0N6d3AKrZQWVebwDC7Nd' + 'eL4sDW8AkaXuBIrbuZw6XUHd6WI0NvU/q10j2qMm0YoXSu+dExou1/1THwx5g86MxcX5nwodKUEVCOTzKMyrLz' + 'CRsitH/+dAXhZNRp/FbnDCGBMyD3MOYCjZvAFbCZUasoRwqponxILw==' + ) + self.daemon.jsonrpc_sync_apply('password', data=add_pref) + self.assertEqual(self.daemon.jsonrpc_sync_hash(), hash_w_pref) + self.assertEqual(self.daemon.jsonrpc_preference_get(), {"fruit": ["apple", "orange"]}) + + self.daemon.jsonrpc_preference_set("fruit", ["peach", "apricot"]) + self.assertEqual(self.daemon.jsonrpc_preference_get(), {"fruit": ["peach", "apricot"]}) + self.assertNotEqual(self.daemon.jsonrpc_sync_hash(), hash_w_pref)