We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Upgrade to Premium popup still displayed when created user with Premium api key 
Created docker image from commit https://github.com/rotki/rotki/commit/a7ef8be31f8059eeff34509667688a87a63edfc6 and created user for a first time importing api keys i was present with `Upgrade to Premium` popup, expected no to see this as far as Premium keys already were imported during user creation. 

<img width="797" alt="Screen Shot 2021-05-05 at 6 40 03 PM" src="https://user-images.githubusercontent.com/15807643/117230156-fb5d4880-ae0b-11eb-9bc1-0d1d11cb5c6c.png">
<img width="1888" alt="Screen Shot 2021-05-05 at 6 43 32 PM" src="https://user-images.githubusercontent.com/15807643/117230372-79215400-ae0c-11eb-8f24-dbe79a38bab2.png">

Imported api keys second time to activate Premium
<img width="1814" alt="Screen Shot 2021-05-05 at 6 44 45 PM" src="https://user-images.githubusercontent.com/15807643/117230452-a66e0200-ae0c-11eb-9c3a-d1ab8be0be9a.png">




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rotkehlchen/premium/sync.py`
Content:
```
1 import base64
2 import logging
3 import shutil
4 from enum import Enum
5 from typing import Any, Dict, NamedTuple, Optional, Tuple
6 
7 from typing_extensions import Literal
8 
9 from rotkehlchen.data_handler import DataHandler
10 from rotkehlchen.errors import (
11     PremiumAuthenticationError,
12     RemoteError,
13     RotkehlchenPermissionError,
14     UnableToDecryptRemoteData,
15 )
16 from rotkehlchen.logging import RotkehlchenLogsAdapter
17 from rotkehlchen.premium.premium import Premium, PremiumCredentials, premium_create_and_verify
18 from rotkehlchen.utils.misc import timestamp_to_date, ts_now
19 
20 logger = logging.getLogger(__name__)
21 log = RotkehlchenLogsAdapter(logger)
22 
23 
24 class CanSync(Enum):
25     YES = 0
26     NO = 1
27     ASK_USER = 2
28 
29 
30 class SyncCheckResult(NamedTuple):
31     # The result of the sync check
32     can_sync: CanSync
33     # If result is ASK_USER, what should the message be?
34     message: str
35     payload: Optional[Dict[str, Any]]
36 
37 
38 class PremiumSyncManager():
39 
40     def __init__(self, data: DataHandler, password: str) -> None:
41         # Initialize this with the value saved in the DB
42         self.last_data_upload_ts = data.db.get_last_data_upload_ts()
43         self.data = data
44         self.password = password
45         self.premium: Optional[Premium] = None
46 
47     def _can_sync_data_from_server(self, new_account: bool) -> SyncCheckResult:
48         """
49         Checks if the remote data can be pulled from the server.
50 
51         Returns a SyncCheckResult denoting whether we can pull for sure,
52         whether we can't pull or whether the user should be asked. If the user
53         should be asked a message is also returned
54         """
55         log.debug('can sync data from server -- start')
56         if self.premium is None:
57             return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)
58 
59         b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)
60 
61         try:
62             metadata = self.premium.query_last_data_metadata()
63         except RemoteError as e:
64             log.debug('can sync data from server failed', error=str(e))
65             return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)
66 
67         if new_account:
68             return SyncCheckResult(can_sync=CanSync.YES, message='', payload=None)
69 
70         if not self.data.db.get_premium_sync():
71             # If it's not a new account and the db setting for premium syncing is off stop
72             return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)
73 
74         log.debug(
75             'CAN_PULL',
76             ours=our_hash,
77             theirs=metadata.data_hash,
78         )
79         if our_hash == metadata.data_hash:
80             log.debug('sync from server stopped -- same hash')
81             # same hash -- no need to get anything
82             return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)
83 
84         our_last_write_ts = self.data.db.get_last_write_ts()
85         data_bytes_size = len(base64.b64decode(b64_encoded_data))
86 
87         local_more_recent = our_last_write_ts >= metadata.last_modify_ts
88         local_bigger = data_bytes_size >= metadata.data_size
89 
90         if local_more_recent and local_bigger:
91             log.debug('sync from server stopped -- local is both newer and bigger')
92             return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)
93 
94         if local_more_recent is False:  # remote is more recent
95             message = (
96                 'Detected remote database with more recent modification timestamp '
97                 'than the local one. '
98             )
99         else:  # remote is bigger
100             message = 'Detected remote database with bigger size than the local one. '
101 
102         return SyncCheckResult(
103             can_sync=CanSync.ASK_USER,
104             message=message,
105             payload={
106                 'local_size': data_bytes_size,
107                 'remote_size': metadata.data_size,
108                 'local_last_modified': timestamp_to_date(our_last_write_ts),
109                 'remote_last_modified': timestamp_to_date(metadata.last_modify_ts),
110             },
111         )
112 
113     def _sync_data_from_server_and_replace_local(self) -> Tuple[bool, str]:
114         """
115         Performs syncing of data from server and replaces local db
116 
117         Returns true for success and False for error/failure
118 
119         May raise:
120         - PremiumAuthenticationError due to an UnableToDecryptRemoteData
121         coming from  decompress_and_decrypt_db. This happens when the given password
122         does not match the one on the saved DB.
123         """
124         if self.premium is None:
125             return False, 'Pulling failed. User does not have active premium.'
126 
127         try:
128             result = self.premium.pull_data()
129         except RemoteError as e:
130             log.debug('sync from server -- pulling failed.', error=str(e))
131             return False, f'Pulling failed: {str(e)}'
132 
133         if result['data'] is None:
134             log.debug('sync from server -- no data found.')
135             return False, 'No data found'
136 
137         try:
138             self.data.decompress_and_decrypt_db(self.password, result['data'])
139         except UnableToDecryptRemoteData as e:
140             raise PremiumAuthenticationError(
141                 'The given password can not unlock the database that was retrieved  from '
142                 'the server. Make sure to use the same password as when the account was created.',
143             ) from e
144 
145         return True, ''
146 
147     def maybe_upload_data_to_server(self, force_upload: bool = False) -> bool:
148         # if user has no premium do nothing
149         if self.premium is None:
150             return False
151 
152         if not self.data.db.get_premium_sync() and not force_upload:
153             return False
154 
155         # upload only once per hour
156         diff = ts_now() - self.last_data_upload_ts
157         if diff < 3600 and not force_upload:
158             return False
159 
160         try:
161             metadata = self.premium.query_last_data_metadata()
162         except RemoteError as e:
163             log.debug('upload to server -- fetching metadata error', error=str(e))
164             return False
165         b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)
166 
167         log.debug(
168             'CAN_PUSH',
169             ours=our_hash,
170             theirs=metadata.data_hash,
171         )
172         if our_hash == metadata.data_hash and not force_upload:
173             log.debug('upload to server stopped -- same hash')
174             # same hash -- no need to upload anything
175             return False
176 
177         our_last_write_ts = self.data.db.get_last_write_ts()
178         if our_last_write_ts <= metadata.last_modify_ts and not force_upload:
179             # Server's DB was modified after our local DB
180             log.debug(
181                 f'upload to server stopped -- remote db({metadata.last_modify_ts}) '
182                 f'more recent than local({our_last_write_ts})',
183             )
184             return False
185 
186         data_bytes_size = len(base64.b64decode(b64_encoded_data))
187         if data_bytes_size < metadata.data_size and not force_upload:
188             # Let's be conservative.
189             # TODO: Here perhaps prompt user in the future
190             log.debug(
191                 f'upload to server stopped -- remote db({metadata.data_size}) '
192                 f'bigger than local({data_bytes_size})',
193             )
194             return False
195 
196         try:
197             self.premium.upload_data(
198                 data_blob=b64_encoded_data,
199                 our_hash=our_hash,
200                 last_modify_ts=our_last_write_ts,
201                 compression_type='zlib',
202             )
203         except RemoteError as e:
204             log.debug('upload to server -- upload error', error=str(e))
205             return False
206 
207         # update the last data upload value
208         self.last_data_upload_ts = ts_now()
209         self.data.db.update_last_data_upload_ts(self.last_data_upload_ts)
210         log.debug('upload to server -- success')
211         return True
212 
213     def sync_data(self, action: Literal['upload', 'download']) -> Tuple[bool, str]:
214         msg = ''
215 
216         if action == 'upload':
217             success = self.maybe_upload_data_to_server(force_upload=True)
218 
219             if not success:
220                 msg = 'Upload failed'
221             return success, msg
222 
223         return self._sync_data_from_server_and_replace_local()
224 
225     def try_premium_at_start(
226             self,
227             given_premium_credentials: Optional[PremiumCredentials],
228             username: str,
229             create_new: bool,
230             sync_approval: Literal['yes', 'no', 'unknown'],
231     ) -> Optional[Premium]:
232         """
233         Check if new user provided api pair or we already got one in the DB
234 
235         Returns the created premium if user's premium credentials were fine.
236 
237         If not it will raise PremiumAuthenticationError.
238 
239         If no credentials were given it returns None
240         """
241 
242         if given_premium_credentials is not None:
243             assert create_new, 'We should never get here for an already existing account'
244 
245             try:
246                 self.premium = premium_create_and_verify(given_premium_credentials)
247             except PremiumAuthenticationError as e:
248                 log.error('Given API key is invalid')
249                 # At this point we are at a new user trying to create an account with
250                 # premium API keys and we failed. But a directory was created. Remove it.
251                 # But create a backup of it in case something went really wrong
252                 # and the directory contained data we did not want to lose
253                 shutil.move(
254                     self.data.user_data_dir,  # type: ignore
255                     self.data.data_directory / f'auto_backup_{username}_{ts_now()}',
256                 )
257                 raise PremiumAuthenticationError(
258                     'Could not verify keys for the new account. '
259                     '{}'.format(str(e)),
260                 ) from e
261 
262         # else, if we got premium data in the DB initialize it and try to sync with the server
263         db_credentials = self.data.db.get_rotkehlchen_premium()
264         if db_credentials:
265             assert not create_new, 'We should never get here for a new account'
266             try:
267                 self.premium = premium_create_and_verify(db_credentials)
268             except PremiumAuthenticationError as e:
269                 message = (
270                     f'Could not authenticate with the rotkehlchen server with '
271                     f'the API keys found in the Database. Error: {str(e)}'
272                 )
273                 log.error(message)
274                 raise PremiumAuthenticationError(message) from e
275 
276         if self.premium is None:
277             return None
278 
279         result = self._can_sync_data_from_server(new_account=create_new)
280         if result.can_sync == CanSync.ASK_USER:
281             if sync_approval == 'unknown':
282                 log.info('Remote DB is possibly newer. Ask user.')
283                 raise RotkehlchenPermissionError(result.message, result.payload)
284 
285             if sync_approval == 'yes':
286                 log.info('User approved data sync from server')
287                 if self._sync_data_from_server_and_replace_local()[0]:
288                     if create_new:
289                         # if we successfully synced data from the server and this is
290                         # a new account, make sure the api keys are properly stored
291                         # in the DB
292                         self.data.db.set_rotkehlchen_premium(self.premium.credentials)
293 
294             else:
295                 log.debug('Could sync data from server but user refused')
296         elif result.can_sync == CanSync.YES:
297             log.info('User approved data sync from server')
298             if self._sync_data_from_server_and_replace_local()[0]:
299                 if create_new:
300                     # if we successfully synced data from the server and this is
301                     # a new account, make sure the api keys are properly stored
302                     # in the DB
303                     self.data.db.set_rotkehlchen_premium(self.premium.credentials)
304 
305         # else result.can_sync was no, so we do nothing
306 
307         # Success, return premium
308         return self.premium
309
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rotkehlchen/premium/sync.py b/rotkehlchen/premium/sync.py
--- a/rotkehlchen/premium/sync.py
+++ b/rotkehlchen/premium/sync.py
@@ -284,25 +284,18 @@
 
             if sync_approval == 'yes':
                 log.info('User approved data sync from server')
-                if self._sync_data_from_server_and_replace_local()[0]:
-                    if create_new:
-                        # if we successfully synced data from the server and this is
-                        # a new account, make sure the api keys are properly stored
-                        # in the DB
-                        self.data.db.set_rotkehlchen_premium(self.premium.credentials)
+                self._sync_data_from_server_and_replace_local()  # this may raise due to password
 
             else:
                 log.debug('Could sync data from server but user refused')
         elif result.can_sync == CanSync.YES:
             log.info('User approved data sync from server')
-            if self._sync_data_from_server_and_replace_local()[0]:
-                if create_new:
-                    # if we successfully synced data from the server and this is
-                    # a new account, make sure the api keys are properly stored
-                    # in the DB
-                    self.data.db.set_rotkehlchen_premium(self.premium.credentials)
-
-        # else result.can_sync was no, so we do nothing
+            self._sync_data_from_server_and_replace_local()  # this may raise due to password
+
+        if create_new:
+            # if this is a new account, make sure the api keys are properly stored
+            # in the DB
+            self.data.db.set_rotkehlchen_premium(self.premium.credentials)
 
         # Success, return premium
         return self.premium

{"golden_diff": "diff --git a/rotkehlchen/premium/sync.py b/rotkehlchen/premium/sync.py\n--- a/rotkehlchen/premium/sync.py\n+++ b/rotkehlchen/premium/sync.py\n@@ -284,25 +284,18 @@\n \n             if sync_approval == 'yes':\n                 log.info('User approved data sync from server')\n-                if self._sync_data_from_server_and_replace_local()[0]:\n-                    if create_new:\n-                        # if we successfully synced data from the server and this is\n-                        # a new account, make sure the api keys are properly stored\n-                        # in the DB\n-                        self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n+                self._sync_data_from_server_and_replace_local()  # this may raise due to password\n \n             else:\n                 log.debug('Could sync data from server but user refused')\n         elif result.can_sync == CanSync.YES:\n             log.info('User approved data sync from server')\n-            if self._sync_data_from_server_and_replace_local()[0]:\n-                if create_new:\n-                    # if we successfully synced data from the server and this is\n-                    # a new account, make sure the api keys are properly stored\n-                    # in the DB\n-                    self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n-\n-        # else result.can_sync was no, so we do nothing\n+            self._sync_data_from_server_and_replace_local()  # this may raise due to password\n+\n+        if create_new:\n+            # if this is a new account, make sure the api keys are properly stored\n+            # in the DB\n+            self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n \n         # Success, return premium\n         return self.premium\n", "issue": "Upgrade to Premium popup still displayed when created user with Premium api key \nCreated docker image from commit https://github.com/rotki/rotki/commit/a7ef8be31f8059eeff34509667688a87a63edfc6 and created user for a first time importing api keys i was present with `Upgrade to Premium` popup, expected no to see this as far as Premium keys already were imported during user creation. \r\n\r\n<img width=\"797\" alt=\"Screen Shot 2021-05-05 at 6 40 03 PM\" src=\"https://user-images.githubusercontent.com/15807643/117230156-fb5d4880-ae0b-11eb-9bc1-0d1d11cb5c6c.png\">\r\n<img width=\"1888\" alt=\"Screen Shot 2021-05-05 at 6 43 32 PM\" src=\"https://user-images.githubusercontent.com/15807643/117230372-79215400-ae0c-11eb-8f24-dbe79a38bab2.png\">\r\n\r\nImported api keys second time to activate Premium\r\n<img width=\"1814\" alt=\"Screen Shot 2021-05-05 at 6 44 45 PM\" src=\"https://user-images.githubusercontent.com/15807643/117230452-a66e0200-ae0c-11eb-9c3a-d1ab8be0be9a.png\">\r\n\r\n\r\n\n", "before_files": [{"content": "import base64\nimport logging\nimport shutil\nfrom enum import Enum\nfrom typing import Any, Dict, NamedTuple, Optional, Tuple\n\nfrom typing_extensions import Literal\n\nfrom rotkehlchen.data_handler import DataHandler\nfrom rotkehlchen.errors import (\n    PremiumAuthenticationError,\n    RemoteError,\n    RotkehlchenPermissionError,\n    UnableToDecryptRemoteData,\n)\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.premium.premium import Premium, PremiumCredentials, premium_create_and_verify\nfrom rotkehlchen.utils.misc import timestamp_to_date, ts_now\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\n\nclass CanSync(Enum):\n    YES = 0\n    NO = 1\n    ASK_USER = 2\n\n\nclass SyncCheckResult(NamedTuple):\n    # The result of the sync check\n    can_sync: CanSync\n    # If result is ASK_USER, what should the message be?\n    message: str\n    payload: Optional[Dict[str, Any]]\n\n\nclass PremiumSyncManager():\n\n    def __init__(self, data: DataHandler, password: str) -> None:\n        # Initialize this with the value saved in the DB\n        self.last_data_upload_ts = data.db.get_last_data_upload_ts()\n        self.data = data\n        self.password = password\n        self.premium: Optional[Premium] = None\n\n    def _can_sync_data_from_server(self, new_account: bool) -> SyncCheckResult:\n        \"\"\"\n        Checks if the remote data can be pulled from the server.\n\n        Returns a SyncCheckResult denoting whether we can pull for sure,\n        whether we can't pull or whether the user should be asked. If the user\n        should be asked a message is also returned\n        \"\"\"\n        log.debug('can sync data from server -- start')\n        if self.premium is None:\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)\n\n        try:\n            metadata = self.premium.query_last_data_metadata()\n        except RemoteError as e:\n            log.debug('can sync data from server failed', error=str(e))\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        if new_account:\n            return SyncCheckResult(can_sync=CanSync.YES, message='', payload=None)\n\n        if not self.data.db.get_premium_sync():\n            # If it's not a new account and the db setting for premium syncing is off stop\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        log.debug(\n            'CAN_PULL',\n            ours=our_hash,\n            theirs=metadata.data_hash,\n        )\n        if our_hash == metadata.data_hash:\n            log.debug('sync from server stopped -- same hash')\n            # same hash -- no need to get anything\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        our_last_write_ts = self.data.db.get_last_write_ts()\n        data_bytes_size = len(base64.b64decode(b64_encoded_data))\n\n        local_more_recent = our_last_write_ts >= metadata.last_modify_ts\n        local_bigger = data_bytes_size >= metadata.data_size\n\n        if local_more_recent and local_bigger:\n            log.debug('sync from server stopped -- local is both newer and bigger')\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        if local_more_recent is False:  # remote is more recent\n            message = (\n                'Detected remote database with more recent modification timestamp '\n                'than the local one. '\n            )\n        else:  # remote is bigger\n            message = 'Detected remote database with bigger size than the local one. '\n\n        return SyncCheckResult(\n            can_sync=CanSync.ASK_USER,\n            message=message,\n            payload={\n                'local_size': data_bytes_size,\n                'remote_size': metadata.data_size,\n                'local_last_modified': timestamp_to_date(our_last_write_ts),\n                'remote_last_modified': timestamp_to_date(metadata.last_modify_ts),\n            },\n        )\n\n    def _sync_data_from_server_and_replace_local(self) -> Tuple[bool, str]:\n        \"\"\"\n        Performs syncing of data from server and replaces local db\n\n        Returns true for success and False for error/failure\n\n        May raise:\n        - PremiumAuthenticationError due to an UnableToDecryptRemoteData\n        coming from  decompress_and_decrypt_db. This happens when the given password\n        does not match the one on the saved DB.\n        \"\"\"\n        if self.premium is None:\n            return False, 'Pulling failed. User does not have active premium.'\n\n        try:\n            result = self.premium.pull_data()\n        except RemoteError as e:\n            log.debug('sync from server -- pulling failed.', error=str(e))\n            return False, f'Pulling failed: {str(e)}'\n\n        if result['data'] is None:\n            log.debug('sync from server -- no data found.')\n            return False, 'No data found'\n\n        try:\n            self.data.decompress_and_decrypt_db(self.password, result['data'])\n        except UnableToDecryptRemoteData as e:\n            raise PremiumAuthenticationError(\n                'The given password can not unlock the database that was retrieved  from '\n                'the server. Make sure to use the same password as when the account was created.',\n            ) from e\n\n        return True, ''\n\n    def maybe_upload_data_to_server(self, force_upload: bool = False) -> bool:\n        # if user has no premium do nothing\n        if self.premium is None:\n            return False\n\n        if not self.data.db.get_premium_sync() and not force_upload:\n            return False\n\n        # upload only once per hour\n        diff = ts_now() - self.last_data_upload_ts\n        if diff < 3600 and not force_upload:\n            return False\n\n        try:\n            metadata = self.premium.query_last_data_metadata()\n        except RemoteError as e:\n            log.debug('upload to server -- fetching metadata error', error=str(e))\n            return False\n        b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)\n\n        log.debug(\n            'CAN_PUSH',\n            ours=our_hash,\n            theirs=metadata.data_hash,\n        )\n        if our_hash == metadata.data_hash and not force_upload:\n            log.debug('upload to server stopped -- same hash')\n            # same hash -- no need to upload anything\n            return False\n\n        our_last_write_ts = self.data.db.get_last_write_ts()\n        if our_last_write_ts <= metadata.last_modify_ts and not force_upload:\n            # Server's DB was modified after our local DB\n            log.debug(\n                f'upload to server stopped -- remote db({metadata.last_modify_ts}) '\n                f'more recent than local({our_last_write_ts})',\n            )\n            return False\n\n        data_bytes_size = len(base64.b64decode(b64_encoded_data))\n        if data_bytes_size < metadata.data_size and not force_upload:\n            # Let's be conservative.\n            # TODO: Here perhaps prompt user in the future\n            log.debug(\n                f'upload to server stopped -- remote db({metadata.data_size}) '\n                f'bigger than local({data_bytes_size})',\n            )\n            return False\n\n        try:\n            self.premium.upload_data(\n                data_blob=b64_encoded_data,\n                our_hash=our_hash,\n                last_modify_ts=our_last_write_ts,\n                compression_type='zlib',\n            )\n        except RemoteError as e:\n            log.debug('upload to server -- upload error', error=str(e))\n            return False\n\n        # update the last data upload value\n        self.last_data_upload_ts = ts_now()\n        self.data.db.update_last_data_upload_ts(self.last_data_upload_ts)\n        log.debug('upload to server -- success')\n        return True\n\n    def sync_data(self, action: Literal['upload', 'download']) -> Tuple[bool, str]:\n        msg = ''\n\n        if action == 'upload':\n            success = self.maybe_upload_data_to_server(force_upload=True)\n\n            if not success:\n                msg = 'Upload failed'\n            return success, msg\n\n        return self._sync_data_from_server_and_replace_local()\n\n    def try_premium_at_start(\n            self,\n            given_premium_credentials: Optional[PremiumCredentials],\n            username: str,\n            create_new: bool,\n            sync_approval: Literal['yes', 'no', 'unknown'],\n    ) -> Optional[Premium]:\n        \"\"\"\n        Check if new user provided api pair or we already got one in the DB\n\n        Returns the created premium if user's premium credentials were fine.\n\n        If not it will raise PremiumAuthenticationError.\n\n        If no credentials were given it returns None\n        \"\"\"\n\n        if given_premium_credentials is not None:\n            assert create_new, 'We should never get here for an already existing account'\n\n            try:\n                self.premium = premium_create_and_verify(given_premium_credentials)\n            except PremiumAuthenticationError as e:\n                log.error('Given API key is invalid')\n                # At this point we are at a new user trying to create an account with\n                # premium API keys and we failed. But a directory was created. Remove it.\n                # But create a backup of it in case something went really wrong\n                # and the directory contained data we did not want to lose\n                shutil.move(\n                    self.data.user_data_dir,  # type: ignore\n                    self.data.data_directory / f'auto_backup_{username}_{ts_now()}',\n                )\n                raise PremiumAuthenticationError(\n                    'Could not verify keys for the new account. '\n                    '{}'.format(str(e)),\n                ) from e\n\n        # else, if we got premium data in the DB initialize it and try to sync with the server\n        db_credentials = self.data.db.get_rotkehlchen_premium()\n        if db_credentials:\n            assert not create_new, 'We should never get here for a new account'\n            try:\n                self.premium = premium_create_and_verify(db_credentials)\n            except PremiumAuthenticationError as e:\n                message = (\n                    f'Could not authenticate with the rotkehlchen server with '\n                    f'the API keys found in the Database. Error: {str(e)}'\n                )\n                log.error(message)\n                raise PremiumAuthenticationError(message) from e\n\n        if self.premium is None:\n            return None\n\n        result = self._can_sync_data_from_server(new_account=create_new)\n        if result.can_sync == CanSync.ASK_USER:\n            if sync_approval == 'unknown':\n                log.info('Remote DB is possibly newer. Ask user.')\n                raise RotkehlchenPermissionError(result.message, result.payload)\n\n            if sync_approval == 'yes':\n                log.info('User approved data sync from server')\n                if self._sync_data_from_server_and_replace_local()[0]:\n                    if create_new:\n                        # if we successfully synced data from the server and this is\n                        # a new account, make sure the api keys are properly stored\n                        # in the DB\n                        self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n\n            else:\n                log.debug('Could sync data from server but user refused')\n        elif result.can_sync == CanSync.YES:\n            log.info('User approved data sync from server')\n            if self._sync_data_from_server_and_replace_local()[0]:\n                if create_new:\n                    # if we successfully synced data from the server and this is\n                    # a new account, make sure the api keys are properly stored\n                    # in the DB\n                    self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n\n        # else result.can_sync was no, so we do nothing\n\n        # Success, return premium\n        return self.premium\n", "path": "rotkehlchen/premium/sync.py"}], "after_files": [{"content": "import base64\nimport logging\nimport shutil\nfrom enum import Enum\nfrom typing import Any, Dict, NamedTuple, Optional, Tuple\n\nfrom typing_extensions import Literal\n\nfrom rotkehlchen.data_handler import DataHandler\nfrom rotkehlchen.errors import (\n    PremiumAuthenticationError,\n    RemoteError,\n    RotkehlchenPermissionError,\n    UnableToDecryptRemoteData,\n)\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.premium.premium import Premium, PremiumCredentials, premium_create_and_verify\nfrom rotkehlchen.utils.misc import timestamp_to_date, ts_now\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\n\nclass CanSync(Enum):\n    YES = 0\n    NO = 1\n    ASK_USER = 2\n\n\nclass SyncCheckResult(NamedTuple):\n    # The result of the sync check\n    can_sync: CanSync\n    # If result is ASK_USER, what should the message be?\n    message: str\n    payload: Optional[Dict[str, Any]]\n\n\nclass PremiumSyncManager():\n\n    def __init__(self, data: DataHandler, password: str) -> None:\n        # Initialize this with the value saved in the DB\n        self.last_data_upload_ts = data.db.get_last_data_upload_ts()\n        self.data = data\n        self.password = password\n        self.premium: Optional[Premium] = None\n\n    def _can_sync_data_from_server(self, new_account: bool) -> SyncCheckResult:\n        \"\"\"\n        Checks if the remote data can be pulled from the server.\n\n        Returns a SyncCheckResult denoting whether we can pull for sure,\n        whether we can't pull or whether the user should be asked. If the user\n        should be asked a message is also returned\n        \"\"\"\n        log.debug('can sync data from server -- start')\n        if self.premium is None:\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)\n\n        try:\n            metadata = self.premium.query_last_data_metadata()\n        except RemoteError as e:\n            log.debug('can sync data from server failed', error=str(e))\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        if new_account:\n            return SyncCheckResult(can_sync=CanSync.YES, message='', payload=None)\n\n        if not self.data.db.get_premium_sync():\n            # If it's not a new account and the db setting for premium syncing is off stop\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        log.debug(\n            'CAN_PULL',\n            ours=our_hash,\n            theirs=metadata.data_hash,\n        )\n        if our_hash == metadata.data_hash:\n            log.debug('sync from server stopped -- same hash')\n            # same hash -- no need to get anything\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        our_last_write_ts = self.data.db.get_last_write_ts()\n        data_bytes_size = len(base64.b64decode(b64_encoded_data))\n\n        local_more_recent = our_last_write_ts >= metadata.last_modify_ts\n        local_bigger = data_bytes_size >= metadata.data_size\n\n        if local_more_recent and local_bigger:\n            log.debug('sync from server stopped -- local is both newer and bigger')\n            return SyncCheckResult(can_sync=CanSync.NO, message='', payload=None)\n\n        if local_more_recent is False:  # remote is more recent\n            message = (\n                'Detected remote database with more recent modification timestamp '\n                'than the local one. '\n            )\n        else:  # remote is bigger\n            message = 'Detected remote database with bigger size than the local one. '\n\n        return SyncCheckResult(\n            can_sync=CanSync.ASK_USER,\n            message=message,\n            payload={\n                'local_size': data_bytes_size,\n                'remote_size': metadata.data_size,\n                'local_last_modified': timestamp_to_date(our_last_write_ts),\n                'remote_last_modified': timestamp_to_date(metadata.last_modify_ts),\n            },\n        )\n\n    def _sync_data_from_server_and_replace_local(self) -> Tuple[bool, str]:\n        \"\"\"\n        Performs syncing of data from server and replaces local db\n\n        Returns true for success and False for error/failure\n\n        May raise:\n        - PremiumAuthenticationError due to an UnableToDecryptRemoteData\n        coming from  decompress_and_decrypt_db. This happens when the given password\n        does not match the one on the saved DB.\n        \"\"\"\n        if self.premium is None:\n            return False, 'Pulling failed. User does not have active premium.'\n\n        try:\n            result = self.premium.pull_data()\n        except RemoteError as e:\n            log.debug('sync from server -- pulling failed.', error=str(e))\n            return False, f'Pulling failed: {str(e)}'\n\n        if result['data'] is None:\n            log.debug('sync from server -- no data found.')\n            return False, 'No data found'\n\n        try:\n            self.data.decompress_and_decrypt_db(self.password, result['data'])\n        except UnableToDecryptRemoteData as e:\n            raise PremiumAuthenticationError(\n                'The given password can not unlock the database that was retrieved  from '\n                'the server. Make sure to use the same password as when the account was created.',\n            ) from e\n\n        return True, ''\n\n    def maybe_upload_data_to_server(self, force_upload: bool = False) -> bool:\n        # if user has no premium do nothing\n        if self.premium is None:\n            return False\n\n        if not self.data.db.get_premium_sync() and not force_upload:\n            return False\n\n        # upload only once per hour\n        diff = ts_now() - self.last_data_upload_ts\n        if diff < 3600 and not force_upload:\n            return False\n\n        try:\n            metadata = self.premium.query_last_data_metadata()\n        except RemoteError as e:\n            log.debug('upload to server -- fetching metadata error', error=str(e))\n            return False\n        b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)\n\n        log.debug(\n            'CAN_PUSH',\n            ours=our_hash,\n            theirs=metadata.data_hash,\n        )\n        if our_hash == metadata.data_hash and not force_upload:\n            log.debug('upload to server stopped -- same hash')\n            # same hash -- no need to upload anything\n            return False\n\n        our_last_write_ts = self.data.db.get_last_write_ts()\n        if our_last_write_ts <= metadata.last_modify_ts and not force_upload:\n            # Server's DB was modified after our local DB\n            log.debug(\n                f'upload to server stopped -- remote db({metadata.last_modify_ts}) '\n                f'more recent than local({our_last_write_ts})',\n            )\n            return False\n\n        data_bytes_size = len(base64.b64decode(b64_encoded_data))\n        if data_bytes_size < metadata.data_size and not force_upload:\n            # Let's be conservative.\n            # TODO: Here perhaps prompt user in the future\n            log.debug(\n                f'upload to server stopped -- remote db({metadata.data_size}) '\n                f'bigger than local({data_bytes_size})',\n            )\n            return False\n\n        try:\n            self.premium.upload_data(\n                data_blob=b64_encoded_data,\n                our_hash=our_hash,\n                last_modify_ts=our_last_write_ts,\n                compression_type='zlib',\n            )\n        except RemoteError as e:\n            log.debug('upload to server -- upload error', error=str(e))\n            return False\n\n        # update the last data upload value\n        self.last_data_upload_ts = ts_now()\n        self.data.db.update_last_data_upload_ts(self.last_data_upload_ts)\n        log.debug('upload to server -- success')\n        return True\n\n    def sync_data(self, action: Literal['upload', 'download']) -> Tuple[bool, str]:\n        msg = ''\n\n        if action == 'upload':\n            success = self.maybe_upload_data_to_server(force_upload=True)\n\n            if not success:\n                msg = 'Upload failed'\n            return success, msg\n\n        return self._sync_data_from_server_and_replace_local()\n\n    def try_premium_at_start(\n            self,\n            given_premium_credentials: Optional[PremiumCredentials],\n            username: str,\n            create_new: bool,\n            sync_approval: Literal['yes', 'no', 'unknown'],\n    ) -> Optional[Premium]:\n        \"\"\"\n        Check if new user provided api pair or we already got one in the DB\n\n        Returns the created premium if user's premium credentials were fine.\n\n        If not it will raise PremiumAuthenticationError.\n\n        If no credentials were given it returns None\n        \"\"\"\n\n        if given_premium_credentials is not None:\n            assert create_new, 'We should never get here for an already existing account'\n\n            try:\n                self.premium = premium_create_and_verify(given_premium_credentials)\n            except PremiumAuthenticationError as e:\n                log.error('Given API key is invalid')\n                # At this point we are at a new user trying to create an account with\n                # premium API keys and we failed. But a directory was created. Remove it.\n                # But create a backup of it in case something went really wrong\n                # and the directory contained data we did not want to lose\n                shutil.move(\n                    self.data.user_data_dir,  # type: ignore\n                    self.data.data_directory / f'auto_backup_{username}_{ts_now()}',\n                )\n                raise PremiumAuthenticationError(\n                    'Could not verify keys for the new account. '\n                    '{}'.format(str(e)),\n                ) from e\n\n        # else, if we got premium data in the DB initialize it and try to sync with the server\n        db_credentials = self.data.db.get_rotkehlchen_premium()\n        if db_credentials:\n            assert not create_new, 'We should never get here for a new account'\n            try:\n                self.premium = premium_create_and_verify(db_credentials)\n            except PremiumAuthenticationError as e:\n                message = (\n                    f'Could not authenticate with the rotkehlchen server with '\n                    f'the API keys found in the Database. Error: {str(e)}'\n                )\n                log.error(message)\n                raise PremiumAuthenticationError(message) from e\n\n        if self.premium is None:\n            return None\n\n        result = self._can_sync_data_from_server(new_account=create_new)\n        if result.can_sync == CanSync.ASK_USER:\n            if sync_approval == 'unknown':\n                log.info('Remote DB is possibly newer. Ask user.')\n                raise RotkehlchenPermissionError(result.message, result.payload)\n\n            if sync_approval == 'yes':\n                log.info('User approved data sync from server')\n                self._sync_data_from_server_and_replace_local()  # this may raise due to password\n\n            else:\n                log.debug('Could sync data from server but user refused')\n        elif result.can_sync == CanSync.YES:\n            log.info('User approved data sync from server')\n            self._sync_data_from_server_and_replace_local()  # this may raise due to password\n\n        if create_new:\n            # if this is a new account, make sure the api keys are properly stored\n            # in the DB\n            self.data.db.set_rotkehlchen_premium(self.premium.credentials)\n\n        # Success, return premium\n        return self.premium\n", "path": "rotkehlchen/premium/sync.py"}]}