Exemplo n.º 1
0
    def __init__(
            self,
            trades_number: int,
            seconds_between_trades: int,
            seconds_between_balance_save: int,
            rotkehlchen,
            fake_kraken,
            fake_binance,
    ):
        self.seconds_between_trades = seconds_between_trades
        self.seconds_between_balance_save = seconds_between_balance_save
        self.trades_number = trades_number
        self.current_ts = STARTING_TIMESTAMP
        self.last_trade_ts = 0
        self.last_balance_save_ts = 0
        self.funds = STARTING_FUNDS
        self.rotki = rotkehlchen
        self.kraken = fake_kraken
        self.binance = fake_binance

        timestamp, _, _ = self.get_next_ts()
        for asset, value in self.funds.items():
            if asset.is_fiat():
                self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(
                    asset=asset,
                    label=f'{asset.identifier} balance',
                    amount=value,
                    location=Location.BANKS,
                    tags=None,
                    balance_type=BalanceType.ASSET,
                )])
        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)

        # divide our starting funds between exchanges and keep a part out
        divide_by = len(ALLOWED_EXCHANGES) + 1
        for asset, value in self.funds.items():
            amount = value / divide_by
            for exchange in ALLOWED_EXCHANGES:
                timestamp, _, _ = self.get_next_ts()

                skip_exchange = asset.is_fiat() and exchange != 'kraken'

                if not skip_exchange:
                    getattr(self, exchange).deposit(
                        asset=asset,
                        amount=amount,
                        time=timestamp,
                    )
                if asset.is_fiat():
                    self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(
                        asset=asset,
                        label=f'{asset.identifier} balance {timestamp}',
                        amount=value,
                        location=Location.BANKS,
                        tags=None,
                        balance_type=BalanceType.ASSET,
                    )])

        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)
        self.last_balance_save_ts = timestamp
Exemplo n.º 2
0
def test_deserialize_location(database):
    balances = []
    for idx, data in enumerate(Location):
        assert deserialize_location(str(data)) == data
        balances.append(
            ManuallyTrackedBalance(
                asset=A_BTC,
                label='Test' + str(idx),
                amount=FVal(1),
                location=data,
                tags=None,
            ))

    with pytest.raises(DeserializationError):
        deserialize_location('dsadsad')

    with pytest.raises(DeserializationError):
        deserialize_location(15)

    # Also write and read each location to DB to make sure that
    # location.serialize_for_db() and deserialize_location_from_db work fine
    add_manually_tracked_balances(database, balances)
    balances = database.get_manually_tracked_balances()
    for data in Location:
        assert data in (x.location for x in balances)
Exemplo n.º 3
0
def test_custom_tokens_delete_guard(rotkehlchen_api_server):
    """Test that deleting an owned ethereum token is guarded against"""
    user_db = rotkehlchen_api_server.rest_api.rotkehlchen.data.db
    token0_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[0].ethereum_address
    user_db.add_manually_tracked_balances([ManuallyTrackedBalance(
        asset=Asset(token0_id),
        label='manual1',
        amount=FVal(1),
        location=Location.EXTERNAL,
        tags=None,
        balance_type=BalanceType.ASSET,
    )])

    # Try to delete the token and see it fails because a user owns it
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': INITIAL_TOKENS[0].ethereum_address},
    )
    expected_msg = 'Failed to delete asset with id'
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )
Exemplo n.º 4
0
def test_query_owned_assets(
    rotkehlchen_api_server_with_exchanges,
    ethereum_accounts,
    btc_accounts,
):
    """Test that using the query all owned assets endpoint works"""
    # Disable caching of query results
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    rotki.chain_manager.cache_ttl_secs = 0
    setup = setup_balances(
        rotki=rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=btc_accounts,
        manually_tracked_balances=[
            ManuallyTrackedBalance(
                asset=A_EUR,
                label='My EUR bank',
                amount=FVal('1550'),
                location=Location.BANKS,
                tags=None,
            )
        ],
    )

    # Get all our mocked balances and save them in the DB
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ),
            json={'save_data': True},
        )
    assert_proper_response(response)

    # And now check that the query owned assets endpoint works
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "ownedassetsresource",
            ), )
    assert_proper_response(response)
    data = response.json()
    assert data['message'] == ''
    assert set(data['result']) == {'ETH', 'BTC', 'EUR', 'RDN'}
Exemplo n.º 5
0
def test_custom_asset_delete_guard(rotkehlchen_api_server):
    """Test that deleting an owned asset is guarded against"""
    user_db = rotkehlchen_api_server.rest_api.rotkehlchen.data.db
    custom1 = {
        'asset_type': 'own chain',
        'name': 'foo token',
        'symbol': 'FOO',
        'started': 5,
    }
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'allassetsresource',
        ),
        json=custom1,
    )
    result = assert_proper_response_with_result(response)
    custom1_id = result['identifier']
    user_db.add_manually_tracked_balances([
        ManuallyTrackedBalance(
            id=-1,
            asset=Asset(custom1_id),
            label='manual1',
            amount=FVal(1),
            location=Location.EXTERNAL,
            tags=None,
            balance_type=BalanceType.ASSET,
        )
    ])
    # Try to delete the asset and see it fails because a user owns it
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'allassetsresource',
        ),
        json={'identifier': custom1_id},
    )
    expected_msg = 'Failed to delete asset with id'
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )
Exemplo n.º 6
0
def test_export_import_db(data_dir, username):
    """Create a DB, write some data and then after export/import confirm it's there"""
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)
    starting_balance = ManuallyTrackedBalance(
        asset=A_EUR,
        label='foo',
        amount=FVal(10),
        location=Location.BANKS,
        tags=None,
    )
    data.db.add_manually_tracked_balances([starting_balance])
    encoded_data, _ = data.compress_and_encrypt_db('123')

    # The server would return them decoded
    encoded_data = encoded_data.decode()  # pylint: disable=no-member
    data.decompress_and_decrypt_db('123', encoded_data)
    balances = data.db.get_manually_tracked_balances()
    assert balances == [starting_balance]
Exemplo n.º 7
0
def test_query_all_balances_with_manually_tracked_balances(
    rotkehlchen_api_server_with_exchanges,
    ethereum_accounts,
    btc_accounts,
    manually_tracked_balances,
):
    """Test that using the query all balances endpoint also includes manually tracked balances"""
    # Disable caching of query results
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    rotki.chain_manager.cache_ttl_secs = 0
    manually_tracked_balances = [
        ManuallyTrackedBalance(
            asset=A_BTC,
            label='XPUB BTC wallet',
            amount=FVal('10'),
            location=Location.BLOCKCHAIN,
            tags=None,
        ),
        ManuallyTrackedBalance(
            asset=A_BTC,
            label='BTC in hardware wallet',
            amount=FVal('20'),
            location=Location.BLOCKCHAIN,
            tags=['private'],
        ),
        ManuallyTrackedBalance(
            asset=A_ETH,
            label='ETH in a not supported exchange wallet',
            amount=FVal('10'),
            location=Location.EXTERNAL,
            tags=['private'],
        ),
        ManuallyTrackedBalance(
            asset=A_EUR,
            label='N26 account',
            amount=FVal('12500.15'),
            location=Location.BANKS,
            tags=None,
        ),
        ManuallyTrackedBalance(
            asset=A_EUR,
            label='Deutsche Bank account',
            amount=FVal('1337.1337'),
            location=Location.BANKS,
            tags=None,
        )
    ]
    setup = setup_balances(
        rotki=rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=btc_accounts,
        manually_tracked_balances=manually_tracked_balances,
    )
    # now do the same but save the data in the DB and test it works
    # `save_data` is False by default but data will save since this is a fresh account
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ), )
    result = assert_proper_response_with_result(response)
    assert_all_balances(
        result=result,
        db=rotki.data.db,
        expected_data_in_db=True,
        setup=setup,
    )
Exemplo n.º 8
0
def test_query_all_balances(
    rotkehlchen_api_server_with_exchanges,
    ethereum_accounts,
    btc_accounts,
):
    """Test that using the query all balances endpoint works

    Test that balances from various sources are returned. Such as exchanges,
    blockchain and manually tracked balances"""
    async_query = random.choice([False, True])
    # Disable caching of query results
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    rotki.chain_manager.cache_ttl_secs = 0
    setup = setup_balances(
        rotki=rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=btc_accounts,
        manually_tracked_balances=[
            ManuallyTrackedBalance(
                asset=A_EUR,
                label='My EUR bank',
                amount=FVal('1550'),
                location=Location.BANKS,
                tags=None,
            )
        ],
    )
    # Test that all balances request saves data on a fresh account
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ),
            json={'async_query': async_query},
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task_with_result(
                rotkehlchen_api_server_with_exchanges,
                task_id,
            )
        else:
            outcome = assert_proper_response_with_result(response)

    errors = rotki.msg_aggregator.consume_errors()
    assert len(errors) == 0
    assert_all_balances(
        result=outcome,
        db=rotki.data.db,
        expected_data_in_db=True,
        setup=setup,
    )

    last_save_timestamp = rotki.data.db.get_last_balance_save_time()

    # now do the same but check to see if the balance save frequency delay works
    # and thus data will not be saved
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ), )
    assert_proper_response(response)
    new_save_timestamp = rotki.data.db.get_last_balance_save_time()
    assert last_save_timestamp == new_save_timestamp

    # wait for at least 1 second to make sure that new balances can be saved.
    # Can't save balances again if it's the same timestamp
    gevent.sleep(1)
    # now do the same but test that balance are saved since the balance save frequency delay
    # is overriden via `save_data` = True
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ),
            json={'save_data': True},
        )
    assert_proper_response(response)
    new_save_timestamp = rotki.data.db.get_last_balance_save_time()
    assert last_save_timestamp != new_save_timestamp
Exemplo n.º 9
0
 def make_manually_tracked_balances(  # pylint: disable=no-self-use
     self,
     data: Dict[str, Any],
     **_kwargs: Any,
 ) -> ManuallyTrackedBalance:
     return ManuallyTrackedBalance(**data)
Exemplo n.º 10
0
        )
        msg = 'call count should increase since cache should have been ignored'
        assert all(fn.call_count == 2 for fn in function_call_counters), msg


@pytest.mark.parametrize('tags', [[{
    'name': 'private',
    'description': 'My private accounts',
    'background_color': 'ffffff',
    'foreground_color': '000000',
}]])
@pytest.mark.parametrize('manually_tracked_balances', [[
    ManuallyTrackedBalance(
        asset=A_BTC,
        label='XPUB BTC wallet',
        amount=FVal('10'),
        location=Location.BLOCKCHAIN,
        tags=None,
    ),
    ManuallyTrackedBalance(
        asset=A_BTC,
        label='BTC in hardware wallet',
        amount=FVal('20'),
        location=Location.BLOCKCHAIN,
        tags=['private'],
    ),
    ManuallyTrackedBalance(
        asset=A_ETH,
        label='ETH in a not supported exchange wallet',
        amount=FVal('10'),
        location=Location.EXTERNAL,
Exemplo n.º 11
0
def test_query_statistics_value_distribution(
    rotkehlchen_api_server_with_exchanges,
    ethereum_accounts,
    btc_accounts,
    start_with_valid_premium,
):
    """Test that using the statistics value distribution endpoint works"""
    start_time = ts_now()
    # Disable caching of query results
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    rotki.chain_manager.cache_ttl_secs = 0
    token_balances = {A_RDN: ['111000', '4000000']}
    setup = setup_balances(
        rotki=rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=btc_accounts,
        token_balances=token_balances,
        manually_tracked_balances=[
            ManuallyTrackedBalance(
                asset=A_EUR,
                label='My EUR bank',
                amount=FVal('1550'),
                location=Location.BANKS,
                tags=None,
                balance_type=BalanceType.ASSET,
            )
        ],
    )

    # query balances and save data in DB to have data to test the statistics endpoint
    with ExitStack() as stack:
        setup.enter_all_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server_with_exchanges,
                "allbalancesresource",
            ),
            json={'save_data': True},
        )
    assert_proper_response(response)

    def assert_okay_by_location(response):
        """Helper function to run next query and its assertion twice"""
        if start_with_valid_premium:
            result = assert_proper_response_with_result(response)
            assert len(result) == 5
            locations = {'poloniex', 'binance', 'banks', 'blockchain', 'total'}
            for entry in result:
                assert len(entry) == 3
                assert entry['time'] >= start_time
                assert entry['usd_value'] is not None
                assert entry['location'] in locations
                locations.remove(entry['location'])
            assert len(locations) == 0
        else:
            assert_error_response(
                response=response,
                contained_in_msg=
                'logged in user testuser does not have a premium subscription',
                status_code=HTTPStatus.CONFLICT,
            )

    # and now test that statistics work fine for distribution by location for json body
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsvaluedistributionresource",
        ),
        json={'distribution_by': 'location'},
    )
    assert_okay_by_location(response)
    # and now test that statistics work fine for distribution by location for query params
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsvaluedistributionresource",
        ) + '?distribution_by=location', )
    assert_okay_by_location(response)

    # finally test that statistics work fine for distribution by asset
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsvaluedistributionresource",
        ),
        json={'distribution_by': 'asset'},
    )
    if start_with_valid_premium:
        result = assert_proper_response_with_result(response)
        assert len(result) == 4
        totals = {
            'ETH': get_asset_balance_total(A_ETH, setup),
            'BTC': get_asset_balance_total(A_BTC, setup),
            'EUR': get_asset_balance_total(A_EUR, setup),
            A_RDN.identifier: get_asset_balance_total(A_RDN, setup),
        }
        for entry in result:
            assert len(entry) == 5
            assert entry['time'] >= start_time
            assert entry['category'] == 'asset'
            assert entry['usd_value'] is not None
            assert FVal(entry['amount']) == totals[entry['asset']]
    else:
        assert_error_response(
            response=response,
            contained_in_msg=
            'logged in user testuser does not have a premium subscription',
            status_code=HTTPStatus.CONFLICT,
        )