Repository: rotki/rotki

Issue:
Make the distinction between website account and local account clear
## Problem Definition

A problem that we have been seeing quite a lot is that a lot of users mix up the two different account types that they need with Rotki.

1. The local Rotki account, saved in their desktop
2. The website account whose only reason for existence is to buy and manage a premium subscription

We have added warnings prevent people from getting confused about them. 

1.There is a warning at the website account creation.
![2020-07-30-002315_767x244_scrot](https://user-images.githubusercontent.com/1658405/88859888-f58e6300-d1fa-11ea-9274-6fabc88bf006.png)
2. There is an info window at the application's welcome screen
![2020-07-30-002426_687x151_scrot](https://user-images.githubusercontent.com/1658405/88859955-15be2200-d1fb-11ea-9b89-246c358b1b5f.png)

## Possible approaches

### Terminology

Discord user Cantillon suggested that one of the reasons that probably lead users to confusion is overloading of the same terms `username` and `password` for two very different things.

He suggests that we should consistently use different terms for these two things. This will lead to users not mixing them and at the very least understand we are talking about two different things.

> For example:  maybe "Premium ID" and "Premium website access code"
to leave uname/pw for the app.
>
>Or, vice versa, maybe "Local ID" and "Encryption key" on the app, 
to leave the uname/pw available for your (very standard and typical) website access, that only does the one thing: pay for premium subsctiption.
>
>To be even more clear, and since some users might have >1 Rotki database they intend to keep on their local machine, maybe:
"local DB ID" and "DB encryption key"

### Restrict Website account Creation to only via the APP

@isidorosp had another suggestion. Essentially hide links to account creation so that the user is not able to register at the website by just visiting it.

And to allow account creation only from an iframe inside the application.

## Agreed on approach

TODO: After discussion has concluded the approach should be described here.

## Task

- [ ] Think about, discuss and hone in on a good solution to this problem
- [ ] Execute it


Discussion:
I think that removing the "Register" button from the website does indeed make a lot of sense.

There is no user flow where the user should just go to register in the website without actually wanting to buy a premium subscription. am not sure about restricting account creation to the application. But that button should probably go away.

![2020-07-30-003206_700x84_scrot](https://user-images.githubusercontent.com/1658405/88860879-050eab80-d1fd-11ea-93d3-cf19aa500f8a.png)

Then after the user downloads and uses the application he presses "Get Premium", goes to the [products page](https://rotki.com/products/) and there he sees a link to sign in or register. And that should be good enough.

![2020-07-30-003906_830x179_scrot](https://user-images.githubusercontent.com/1658405/88860941-27a0c480-d1fd-11ea-8880-b213ce4f0ce7.png)


I also like the terminology suggestions but that would mean a bit more work. Removing the "Register" button from the front-page sounds like the biggest win we can have with the least amount of effort.
@aspiers you have given us very nice feedback on the application and I would be really interested to hear your opinion on this ^

@kelsos when you come back from vacation please also chime in.
> I think that removing the "Register" button from the website does indeed make a lot of sense.
> 
> There is no user flow where the user should just go to register in the website without actually wanting to buy a premium subscription. am not sure about restricting account creation to the application. But that button should probably go away.
> 
> ![2020-07-30-003206_700x84_scrot](https://user-images.githubusercontent.com/1658405/88860879-050eab80-d1fd-11ea-93d3-cf19aa500f8a.png)
> 
> Then after the user downloads and uses the application he presses "Get Premium", goes to the [products page](https://rotki.com/products/) and there he sees a link to sign in or register. And that should be good enough.
> 
> ![2020-07-30-003906_830x179_scrot](https://user-images.githubusercontent.com/1658405/88860941-27a0c480-d1fd-11ea-8880-b213ce4f0ce7.png)
> 
> I also like the terminology suggestions but that would mean a bit more work. Removing the "Register" button from the front-page sounds like the biggest win we can have with the least amount of effort.

I think this is a good direction. How about something like this: Remove "Register" and "Sign In" and replace them with "Manage Premium" button that we move off to the side instead of located centrally with the rest of the links. This keeps the # of clicks a user who already has premium needs to get to the sign-in page consistent, but is less ambiguous than "sign in" and "register" w.r.t. what the user will be doing.

Then on that page (which is basically a "Log In to Rotki Premium" page and also has a link to "Create Premium Account") we try to make it a bit more clear that this is for signing into the Rotki Premium account, and a brief explanation/alert of what the difference between the two accounts is. Especially that:
a) Creating a Premium Account before having a local account is not immediately useful
b) The details of one accounts cannot be used to log in to the other, etc.
We can work on creating a different set of terms here to avoid "overloading" of terms like Cantillon suggested. However I would suggest not calling the local password "encryption key". Right now it _happens_ that the local password is also the encryption key, that may not necessarily always be the case (especially if we move to something where like you can use a DID to create an account, the key used for the encryption of data may be something else -- e.g. a specific signed response, or we could use DID details to create a public/private key encryption mechanism).
I think I agree with everything suggested so far.  Great work!  "Premium ID" makes sense, and "Premium website access code" could be shortened to "Premium access code" as it should not be tied exclusively to the website long-term.

See also [#1242 (Clarify local password requirement when setting up 2nd, 3rd, ... machines for Premium syncing)](https://github.com/rotki/rotki/issues/1242).

I also agree with @isidorosp's point that it's probably worth allowing for decoupling the local password from the encryption key at a later date.  This could potentially help with #1242 although I'm not yet convinced about that.
I made some of the suggested changes and deployed them in the website: https://rotki.com/

* Removed Register Button. It's now Manage Premium.
* Added descriptive texts to both login and sign up pages and changed the naming so that it always mentions Premium account and not just account.

![2020-08-09_11-19](https://user-images.githubusercontent.com/1658405/89729054-522b2280-da32-11ea-819d-5a7c920bae32.png)

![2020-08-09_11-19_1](https://user-images.githubusercontent.com/1658405/89729058-58210380-da32-11ea-9de3-33a4541c6e05.png)


Why is the local account needed at all in the first place? All of the target desktops are based on multi-user operating systems. 
> Why is the local account needed at all in the first place? All of the target desktops are based on multi-user operating systems.

Because you may want to have multiple users per OS. Plus the password prompt will be there. Users need to encrypt the DB with something.
> Because you may want to have multiple users per OS. 

Yeah, but each user running rotki will get their data stored on their own home partition, right? Maybe what you mean is that each user will want to have multiple profiles for tracking different things - like personal finances vs their business?

If that is the case, perhaps it would be simpler to have an UI like Firefox? Every user gets a default profile, and those that want to have more than one database can define at settings via a flag.

> Users need to encrypt the DB with something.

That leads to more questions... :)

If the main purpose of Rotki is to *track* information and analyze public data, then what is exactly that needs to be secured at the database? Everything that I've used so far did not require any private key. Is there anything that can only be tracked if the user provides any kind of private keys?
> If that is the case, perhaps it would be simpler to have an UI like Firefox? Every user gets a default profile, and those that want to have more than one database can define at settings via a flag.

That's a good idea. Could you perhaps make a new issue for this? I think we can pursue this.

> If the main purpose of Rotki is to track information and analyze public data, then what is exactly that needs to be secured at the database? Everything that I've used so far did not require any private key. Is there anything that can only be tracked if the user provides any kind of private keys?

Ofcourse! Your entire data history, which accounts you own, public or not, which transactions you made. Once someone gets a hold of your account they will know how much you have and where.

As for keys, yes all of the API keys to contact exchanges are stored in the DB, API keys for external services, API for rotki premium etc.

Resolving patch:
diff --git a/rotkehlchen/chain/ethereum/aave.py b/rotkehlchen/chain/ethereum/aave.py
--- a/rotkehlchen/chain/ethereum/aave.py
+++ b/rotkehlchen/chain/ethereum/aave.py
@@ -1,6 +1,8 @@
 import logging
 from typing import TYPE_CHECKING, Any, Callable, Dict, List, NamedTuple, Optional, Tuple, Union
 
+from gevent.lock import Semaphore
+
 from rotkehlchen.accounting.structures import Balance
 from rotkehlchen.assets.asset import Asset, EthereumToken
 from rotkehlchen.chain.ethereum.makerdao.common import RAY
@@ -31,6 +33,8 @@
 if TYPE_CHECKING:
     from rotkehlchen.chain.ethereum.manager import EthereumManager
 
+MAX_BLOCKTIME_CACHE_AAVE = 250  # 55 mins with 13 secs avg block time
+
 ATOKEN_TO_DEPLOYED_BLOCK = {
     'aETH': 9241088,
     'aDAI': 9241063,
@@ -135,6 +139,8 @@ def __init__(
         self.database = database
         self.msg_aggregator = msg_aggregator
         self.premium = premium
+        self.history_lock = Semaphore()
+        self.balances_lock = Semaphore()
 
     def get_balances(
             self,
@@ -142,6 +148,16 @@ def get_balances(
                 Dict[ChecksumEthAddress, List[DefiProtocolBalances]],
                 Callable[[], Dict[ChecksumEthAddress, List[DefiProtocolBalances]]],
             ],
+    ) -> Dict[ChecksumEthAddress, AaveBalances]:
+        with self.balances_lock:
+            return self._get_balances(given_defi_balances)
+
+    def _get_balances(
+            self,
+            given_defi_balances: Union[
+                Dict[ChecksumEthAddress, List[DefiProtocolBalances]],
+                Callable[[], Dict[ChecksumEthAddress, List[DefiProtocolBalances]]],
+            ],
     ) -> Dict[ChecksumEthAddress, AaveBalances]:
         """Retrieves the aave balances
 
@@ -206,22 +222,24 @@ def get_history(
             addresses: List[ChecksumEthAddress],
             reset_db_data: bool,
     ) -> Dict[ChecksumEthAddress, AaveHistory]:
+        """Detects aave historical data for the given addresses"""
         result = {}
         latest_block = self.ethereum.get_latest_block_number()
+        with self.history_lock:
 
-        if reset_db_data is True:
-            self.database.delete_aave_data()
+            if reset_db_data is True:
+                self.database.delete_aave_data()
 
-        for address in addresses:
-            last_query = self.database.get_used_query_range(f'aave_events_{address}')
-            history_results = self.get_history_for_address(
-                user_address=address,
-                to_block=latest_block,
-                given_from_block=last_query[1] + 1 if last_query is not None else None,
-            )
-            if len(history_results.events) == 0:
-                continue
-            result[address] = history_results
+            for address in addresses:
+                last_query = self.database.get_used_query_range(f'aave_events_{address}')
+                history_results = self.get_history_for_address(
+                    user_address=address,
+                    to_block=latest_block,
+                    given_from_block=last_query[1] + 1 if last_query is not None else None,
+                )
+                if len(history_results.events) == 0:
+                    continue
+                result[address] = history_results
 
         return result
 
@@ -232,13 +250,19 @@ def get_history_for_address(
             atokens_list: Optional[List[EthereumToken]] = None,
             given_from_block: Optional[int] = None,
     ) -> AaveHistory:
+        """
+        Queries aave history for a single address.
+
+        This function should be entered while holding the history_lock
+        semaphore
+        """
         # Get all deposit events for the address
         from_block = AAVE_LENDING_POOL.deployed_block if given_from_block is None else given_from_block  # noqa: E501
         argument_filters = {
             '_user': user_address,
         }
         query_events = True
-        if given_from_block is not None and to_block - given_from_block < 250:  # noqa: E501
+        if given_from_block is not None and to_block - given_from_block < MAX_BLOCKTIME_CACHE_AAVE:  # noqa: E501
             query_events = False  # Save time by not querying events if last query is recent
 
         deposit_events = []
@@ -342,6 +366,8 @@ def get_events_for_atoken_and_address(
             from_block: int,
             to_block: int,
     ) -> List[AaveEvent]:
+        """This function should be entered while holding the history_lock
+        semaphore"""
         argument_filters = {
             'from': ZERO_ADDRESS,
             'to': user_address,


Test patch: