Esempio n. 1
0
def test_query_historical_dsr_with_a_zero_withdrawal(
        rotkehlchen_api_server,
        ethereum_accounts,
        inquirer,  # pylint: disable=unused-argument
):
    """Test DSR for an account that was opened while DSR is 0 and made a 0 DAI withdrawal

    Essentially reproduce DSR problem reported here: https://github.com/rotki/rotki/issues/1032

    The account in question operates in a zero DSR environment but the reported
    problem seems to be just because he tried a zero DAI withdrawal
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    original_get_logs = rotki.chain_manager.ethereum.get_logs
    proxies_mapping = {
        # proxy for 0x714696C5a872611F76655Bc163D0131cBAc60a70
        ethereum_accounts[0]:
        '0xAe9996b76bdAa003ace6D66328A6942565f5768d',
    }
    mock_proxies(rotki, proxies_mapping, 'makerdao_dsr')

    # Query only until a block we know DSR is 0 and we know the number
    # of DSR events
    def mock_get_logs(
            contract_address,
            abi,
            event_name,
            argument_filters,
            from_block,
            to_block='latest',  # pylint: disable=unused-argument
    ):
        return original_get_logs(
            contract_address,
            abi,
            event_name,
            argument_filters,
            from_block,
            to_block=10149816,  # A block at which DSR is still zero
        )

    patched_get_logs = patch.object(
        rotki.chain_manager.ethereum,
        'get_logs',
        side_effect=mock_get_logs,
    )

    with patched_get_logs:
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                "makerdaodsrhistoryresource",
            ))
    assert_proper_response(response)
    json_data = response.json()
    assert json_data['message'] == ''
    result = json_data['result'][ethereum_accounts[0]]
    assert FVal(result['gain_so_far']) == ZERO
    assert FVal(result['gain_so_far_usd_value']) == ZERO
    movements = result['movements']
    expected_movements = [{
        'movement_type':
        'deposit',
        'gain_so_far':
        ZERO,
        'gain_so_far_usd_value':
        ZERO,
        'amount':
        FVal('79'),
        'amount_usd_value':
        FVal('79'),
        'block_number':
        9953028,
        'timestamp':
        1587970286,
        'tx_hash':
        '0x988aea85b54c5b2834b144e9f7628b524bf9faf3b87821aa520b7bcfb57ab289',
    }, {
        'movement_type':
        'withdrawal',
        'gain_so_far':
        ZERO,
        'gain_so_far_usd_value':
        ZERO,
        'amount':
        FVal('79'),
        'amount_usd_value':
        FVal('79'),
        'block_number':
        9968906,
        'timestamp':
        1588182567,
        'tx_hash':
        '0x2a1bee69b9bafe031026dbcc8f199881b568fd767482b5436dd1cd94f2642443',
    }, {
        'movement_type':
        'withdrawal',
        'gain_so_far':
        ZERO,
        'gain_so_far_usd_value':
        ZERO,
        'amount':
        ZERO,
        'amount_usd_value':
        ZERO,
        'block_number':
        9968906,
        'timestamp':
        1588182567,
        'tx_hash':
        '0x618fc9542890a2f58ab20a3c12d173b3638af11fda813e61788e242b4fc9a756',
    }]
    assert_serialized_lists_equal(movements,
                                  expected_movements,
                                  max_diff="1e-26")
    errors = rotki.msg_aggregator.consume_errors()
    assert len(errors) == 0
Esempio n. 2
0
def test_user_creation_with_invalid_premium_credentials(
        rotkehlchen_api_server, data_dir):
    """
    Test that invalid and unauthenticated premium credentials are handled at new user creation
    """
    # Create a user with invalid credentials
    username = '******'
    data = {
        'name': username,
        'password': '******',
        'premium_api_key': 'foo',
        'premium_api_secret': 'boo',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Provided API/Key secret format is invalid',
    )

    # Check that the directory was NOT created
    assert not Path(
        data_dir /
        username).exists(), 'The directory should not have been created'

    # Create a new user with valid but not authenticable credentials
    username = '******'
    data = {
        'name': username,
        'password': '******',
        'premium_api_key': VALID_PREMIUM_KEY,
        'premium_api_secret': VALID_PREMIUM_SECRET,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)

    expected_msg = (
        'Could not verify keys for the new account. Rotkehlchen API key was rejected by server'
    )
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # Check that the directory was NOT created
    assert not Path(
        data_dir /
        username).exists(), 'The directory should not have been created'
    # But check that a backup of the directory was made just in case
    backups = list(Path(data_dir).glob('auto_backup_*'))
    assert len(backups) == 1
    assert 'auto_backup_Anja_' in str(
        backups[0]), 'An automatic backup should have been made'

    # But then try to create a normal-non premium user and see it works
    username = '******'
    data = {
        'name': username,
        'password': '******',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_proper_response(response)
    check_proper_unlock_result(response.json())

    # Query users and make sure the new user is logged in
    response = requests.get(
        api_url_for(rotkehlchen_api_server, "usersresource"))
    assert_proper_response(response)
    json = response.json()
    assert json['result'][username] == 'loggedin'
    assert len(json['result']) == 2

    # Check that the directory was created
    assert Path(data_dir / username / 'rotkehlchen.db').exists()
Esempio n. 3
0
def test_user_password_change(rotkehlchen_api_server, username, db_password):
    """
    Test that changing a logged-in user's users password works successfully and that
    common errors are handled.
    """

    # wrong username
    data_wrong_user = {
        'name': 'billybob',
        'current_password': '******',
        'new_password': '******',
    }
    response = requests.patch(api_url_for(rotkehlchen_api_server,
                                          "userpasswordchangeresource",
                                          name=username),
                              json=data_wrong_user)
    msg = (
        f'Provided user "{data_wrong_user["name"]}" is not the logged in user')
    assert_error_response(
        response=response,
        contained_in_msg=msg,
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong password
    data_wrong_pass = {
        'name': username,
        'current_password': '******',
        'new_password': '******',
    }
    response = requests.patch(api_url_for(rotkehlchen_api_server,
                                          "userpasswordchangeresource",
                                          name=username),
                              json=data_wrong_pass)
    msg = ('Provided current password is not correct')
    assert_error_response(
        response=response,
        contained_in_msg=msg,
        status_code=HTTPStatus.UNAUTHORIZED,
    )

    # success
    data_success = {
        'name': username,
        'current_password': db_password,
        'new_password': '******',
    }
    response = requests.patch(api_url_for(rotkehlchen_api_server,
                                          "userpasswordchangeresource",
                                          name=username),
                              json=data_success)
    assert_simple_ok_response(response)

    # revert password
    data_revert = {
        'name': username,
        'current_password': data_success['new_password'],
        'new_password': db_password,
    }
    response = requests.patch(api_url_for(rotkehlchen_api_server,
                                          "userpasswordchangeresource",
                                          name=username),
                              json=data_revert)
    assert_simple_ok_response(response)
Esempio n. 4
0
def test_query_vaults_usdc(rotkehlchen_api_server, ethereum_accounts):
    """Check vault info and details for a vault with USDC as collateral"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    proxies_mapping = {
        ethereum_accounts[0]:
        '0xBE79958661741079679aFf75DbEd713cE71a979d',  # 7588
    }

    mock_proxies(rotki, proxies_mapping)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultsresource",
        ))
    vaults = assert_proper_response_with_result(response)
    vault_7588 = MakerDAOVault(
        identifier=7588,
        owner=ethereum_accounts[0],
        collateral_type='USDC-A',
        urn='0x56D88244073B2fC17af5B1E6088936D5bAaDc37B',
        collateral_asset=A_USDC,
        collateral_amount=ZERO,
        collateral_usd_value=ZERO,
        debt_value=ZERO,
        collateralization_ratio=None,
        liquidation_ratio=FVal(1.2),
        liquidation_price=None,
        stability_fee=FVal(0.0075),
    )
    expected_vaults = [vault_7588.serialize()]
    assert_serialized_lists_equal(expected_vaults, vaults)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultdetailsresource",
        ))
    vault_7588_details = {
        'identifier':
        7588,
        'creation_ts':
        1585286480,
        'total_interest_owed':
        FVal('0.00050636718'),
        'total_liquidated_amount':
        ZERO,
        'total_liquidated_usd':
        ZERO,
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('45'),
            'amount_usd_value':
            FVal('45'),
            'timestamp':
            1585286480,
            'tx_hash':
            '0x8b553dd0e8ee5385ec91105bf911143666d9df0ecd84c04f288278f7658aa7d6',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('20'),
            'amount_usd_value':
            FVal('20.46'),
            'timestamp':
            1585286480,
            'tx_hash':
            '0x8b553dd0e8ee5385ec91105bf911143666d9df0ecd84c04f288278f7658aa7d6',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('15.99'),
            'amount_usd_value':
            FVal('16.35777'),
            'timestamp':
            1585286769,
            'tx_hash':
            '0xdb861c893a51e4649ff3740cd3658cd4c9b1d048d3b8b4d117f4319bd60aee01',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('35.990506367'),
            'amount_usd_value':
            FVal('36.818288'),
            'timestamp':
            1585290263,
            'tx_hash':
            '0xdd7825fe4a93c6f1ffa25a91b6da2396c229fe16b17242ad5c0bf7962928b2ec',
        }, {
            'event_type':
            'withdraw',
            'amount':
            FVal('45'),
            'amount_usd_value':
            FVal('45'),
            'timestamp':
            1585290300,
            'tx_hash':
            '0x97462ebba7ce2467787bf6de25a25c24e538cf8a647919112c5f048b6a293408',
        }],
    }
    details = assert_proper_response_with_result(response)
    expected_details = [vault_7588_details]
    assert_serialized_lists_equal(expected_details, details)
Esempio n. 5
0
def test_query_vaults_usdc_strange(rotkehlchen_api_server, ethereum_accounts):
    """Strange case of a USDC vault that is not queried correctly

    https://oasis.app/borrow/7538?network=mainnet
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    proxies_mapping = {
        ethereum_accounts[0]:
        '0x15fEaFd4358b8C03c889D6661b0CA1Be3389792F',  # 7538
    }

    mock_proxies(rotki, proxies_mapping)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultsresource",
        ))
    # That proxy has 3 vaults. We only want to test 7538, which is closed/repaid so just keep that
    vaults = [
        x for x in assert_proper_response_with_result(response)
        if x['identifier'] == 7538
    ]
    vault_7538 = MakerDAOVault(
        identifier=7538,
        owner=ethereum_accounts[0],
        collateral_type='USDC-A',
        urn='0x70E58566C7baB6faaFE03fbA69DF45Ef4f48223B',
        collateral_asset=A_USDC,
        collateral_amount=ZERO,
        collateral_usd_value=ZERO,
        debt_value=ZERO,
        collateralization_ratio=None,
        liquidation_ratio=FVal(1.2),
        liquidation_price=None,
    )
    expected_vaults = [vault_7538.serialize()]
    assert_serialized_lists_equal(expected_vaults, vaults)
    # And also make sure that the internal mapping will only query details of 7538
    rotki.chain_manager.makerdao.vault_mappings = {
        ethereum_accounts[0]: [vault_7538]
    }

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultdetailsresource",
        ))
    vault_7538_details = {
        'identifier':
        7538,
        'creation_ts':
        1585145754,
        'total_interest_owed':
        FVal('0.0005943266'),
        'total_liquidated_amount':
        ZERO,
        'total_liquidated_usd':
        ZERO,
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('250.12'),
            'timestamp':
            1588664698,
            'tx_hash':
            '0x9ba4a6187fa2c49ba327e7c923846a08a1e972017ec41d3f9f66ef524f7dde59',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('25'),
            'timestamp':
            1588664698,
            'tx_hash':
            '0x9ba4a6187fa2c49ba327e7c923846a08a1e972017ec41d3f9f66ef524f7dde59',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('25.000248996'),
            'timestamp':
            1588696496,
            'tx_hash':
            '0x8bd960e7eb8b9e2b81d2446d1844dd63f94636c7800ea5e3b4d926ea0244c66c',
        }, {
            'event_type':
            'deposit',
            'amount':
            FVal('0.0113'),
            'timestamp':
            1588720248,
            'tx_hash':
            '0x678c4da562173c102473f1904ff293a767ebac9ec6c7d728ef2fd41acf00a13a',
        }],
    }
    details = assert_proper_response_with_result(response)
    expected_details = [vault_7538_details]
    assert_serialized_lists_equal(expected_details, details)
Esempio n. 6
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,
        )
Esempio n. 7
0
def test_query_vaults_details_liquidation(rotkehlchen_api_server,
                                          ethereum_accounts):
    """Check vault details of a vault with liquidations

    Also use three accounts, two of which have vaults associated with them to test
    that vaults for multiple accounts get detected
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    proxies_mapping = {
        ethereum_accounts[0]: '0x689D4C2229717f877A644A0aAd742D67E5D0a2FB',
        ethereum_accounts[2]: '0x420F88De6dadA0a77Db7b9EdBe3A0C614346031E',
    }
    mock_proxies(rotki, proxies_mapping)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultsresource",
        ))
    vaults = assert_proper_response_with_result(response)
    vault_6021 = {
        'identifier': 6021,
        'owner': ethereum_accounts[2],
        'collateral_type': 'ETH-A',
        'collateral_asset': 'ETH',
        'collateral_amount': ZERO,
        'collateral_usd_value': ZERO,
        'debt_value': ZERO,
        'collateralization_ratio': None,
        'liquidation_ratio': '150.00%',
        'liquidation_price': None,
        'stability_fee': '0.00%',
    }
    vault_8015_with_owner = VAULT_8015.copy()
    vault_8015_with_owner['owner'] = ethereum_accounts[0]
    assert_serialized_dicts_equal(vault_6021, vaults[0])
    assert_serialized_dicts_equal(
        vault_8015_with_owner,
        vaults[1],
        ignore_keys=VAULT_IGNORE_KEYS,
    )
    assert len(vaults) == 2

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultdetailsresource",
        ))
    vault_6021_details = {
        'identifier':
        6021,
        'creation_ts':
        1582699808,
        'total_interest_owed':
        FVal('-11078.655097848869'),
        'total_liquidated_amount':
        FVal('141.7'),
        'total_liquidated_usd':
        FVal('19191.848'),
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('140'),
            'amount_usd_value':
            FVal('31322.2'),
            'timestamp':
            1582699808,
            'tx_hash':
            '0x3246ef91fd3d6e1f7c5766de4fa1f0991ba67d92e518447ba8207fe98569c309',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('14000'),
            'amount_usd_value':
            FVal('14028'),
            'timestamp':
            1582699808,
            'tx_hash':
            '0x3246ef91fd3d6e1f7c5766de4fa1f0991ba67d92e518447ba8207fe98569c309',
        }, {
            'event_type':
            'deposit',
            'amount':
            FVal('1.7'),
            'amount_usd_value':
            FVal('331.262'),
            'timestamp':
            1583958747,
            'tx_hash':
            '0x65ac798cb9f22068e43fd9ef8303a31e436989062ae87e25650cc44c7788ab62',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('2921.344902'),
            'amount_usd_value':
            FVal('2927.187591'),
            'timestamp':
            1584024065,
            'tx_hash':
            '0x6e44d22d6898ee012369787cd75ea6fb9ace6f995cd157675f370e8ba4a7b9ad',
        }, {
            'event_type':
            'liquidation',
            'amount':
            FVal('50'),
            'amount_usd_value':
            FVal('6772'),
            'timestamp':
            1584061534,
            'tx_hash':
            '0xb02050d914ab40f59a9e07eb4f8161ce36eb97cea9c189b027eb1ceeac83a516',
        }, {
            'event_type':
            'liquidation',
            'amount':
            FVal('50'),
            'amount_usd_value':
            FVal('6772'),
            'timestamp':
            1584061897,
            'tx_hash':
            '0x678f31d49dd70d76c0ce441343c0060dc600f4c8dbb4cee2b08c6b451b6097cd',
        }, {
            'event_type':
            'liquidation',
            'amount':
            FVal('41.7'),
            'amount_usd_value':
            FVal('5647.848'),
            'timestamp':
            1584061977,
            'tx_hash':
            '0xded0f9de641087692555d92a7fa94fa9fa7abf22744b2d16c20a66c5e48a8edf',
        }],
    }
    details = assert_proper_response_with_result(response)
    assert len(details) == 2
    assert_serialized_dicts_equal(vault_6021_details, details[0])
    assert_serialized_dicts_equal(VAULT_8015_DETAILS,
                                  details[1],
                                  length_list_keymap={'events': 7})
Esempio n. 8
0
def test_query_asset_movements(rotkehlchen_api_server_with_exchanges):
    """Test that using the asset movements query endpoint works fine"""
    async_query = random.choice([False, True])
    server = rotkehlchen_api_server_with_exchanges
    setup = prepare_rotki_for_history_processing_test(
        server.rest_api.rotkehlchen)
    # setup = mock_history_processing_and_exchanges(server.rest_api.rotkehlchen)
    # query asset movements of one specific exchange
    with setup.polo_patch:
        response = requests.get(
            api_url_for(
                server,
                'assetmovementsresource',
            ),
            json={
                'location': 'poloniex',
                'async_query': async_query
            },
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)
    assert result['entries_found'] == 4
    assert result['entries_limit'] == FREE_ASSET_MOVEMENTS_LIMIT
    poloniex_ids = [x['entry']['identifier'] for x in result['entries']]
    assert_poloniex_asset_movements([x['entry'] for x in result['entries']],
                                    deserialized=True)
    assert all(
        x['ignored_in_accounting'] is False
        for x in result['entries']), 'ignored should be false'  # noqa: E501

    # now let's ignore all poloniex action ids
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            'ignoredactionsresource',
        ),
        json={
            'action_type': 'asset movement',
            'action_ids': poloniex_ids
        },
    )
    result = assert_proper_response_with_result(response)
    assert set(result['asset movement']) == set(poloniex_ids)

    # query asset movements of all exchanges
    with setup.polo_patch:
        response = requests.get(
            api_url_for(server, 'assetmovementsresource'),
            json={'async_query': async_query},
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    movements = result['entries']
    assert_poloniex_asset_movements([
        x['entry'] for x in movements if x['entry']['location'] == 'poloniex'
    ], True)  # noqa: E501
    assert_kraken_asset_movements(
        [x['entry'] for x in movements if x['entry']['location'] == 'kraken'],
        True)  # noqa: E501

    def assert_okay(response):
        """Helper function for DRY checking below assertions"""
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)
        movements = result['entries']
        assert_poloniex_asset_movements(
            to_check_list=[
                x['entry'] for x in movements
                if x['entry']['location'] == 'poloniex'
            ],
            deserialized=True,
            movements_to_check=(1, 2),
        )
        msg = 'poloniex asset movements should have now been ignored for accounting'
        assert all(x['ignored_in_accounting'] is True for x in movements
                   if x['entry']['location'] == 'poloniex'), msg  # noqa: E501
        assert_kraken_asset_movements(
            to_check_list=[
                x['entry'] for x in movements
                if x['entry']['location'] == 'kraken'
            ],
            deserialized=True,
            movements_to_check=(0, 1, 2),
        )

    # and now query them in a specific time range excluding some asset movements
    data = {
        'from_timestamp': 1439994442,
        'to_timestamp': 1458994442,
        'async_query': async_query
    }
    with setup.polo_patch:
        response = requests.get(api_url_for(server, "assetmovementsresource"),
                                json=data)
        assert_okay(response)
    # do the same but with query args. This serves as test of from/to timestamp with query args
    with setup.polo_patch:
        response = requests.get(
            api_url_for(server, "assetmovementsresource") + '?' +
            urlencode(data))
        assert_okay(response)
Esempio n. 9
0
def test_setup_exchange(rotkehlchen_api_server):
    """Test that setting up an exchange via the api works"""
    # Check that no exchanges are registered
    response = requests.get(
        api_url_for(rotkehlchen_api_server, "exchangesresource"))
    assert_proper_response(response)
    json_data = response.json()
    assert json_data['message'] == ''
    assert json_data['result'] == []

    # First test that if api key validation fails we get an error, for every exchange
    for location in SUPPORTED_EXCHANGES:
        data = {
            'location': str(location),
            'name': f'my_{str(location)}',
            'api_key': 'ddddd',
            'api_secret': 'fffffff'
        }  # noqa: E501
        if location in (Location.COINBASEPRO, Location.KUCOIN):
            data['passphrase'] = '123'
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
        assert_error_response(
            response=response,
            contained_in_msg=[
                'Provided API Key or secret is invalid',
                'Provided API Key is invalid',
                'Provided API Key is in invalid Format',
                'Provided API Secret is invalid',
                'Provided Gemini API key needs to have "Auditor" permission activated',
                BITSTAMP_API_KEY_ERROR_CODE_ACTION['API0011'],
                BITFINEX_API_KEY_ERROR_MESSAGE,
                KUCOIN_API_KEY_ERROR_CODE[400003],
                'Bad combination of API Keys',
            ],
            status_code=HTTPStatus.CONFLICT,
        )
    # Make sure that no exchange is registered after that
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    assert len(rotki.exchange_manager.connected_exchanges) == 0

    # Mock the api pair validation and make sure that the exchange is setup
    data = {
        'location': 'kraken',
        'name': 'my_kraken',
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }  # noqa: E501
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_simple_ok_response(response)

    # and check that kraken is now registered
    response = requests.get(
        api_url_for(rotkehlchen_api_server, 'exchangesresource'))
    result = assert_proper_response_with_result(response)
    assert result == [{
        'location': 'kraken',
        'name': 'my_kraken',
        'kraken_account_type': 'starter'
    }]  # noqa: E501

    # Check that we get an error if we try to re-setup an already setup exchange
    data = {
        'location': 'kraken',
        'name': 'my_kraken',
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }  # noqa: E501
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='kraken exchange my_kraken is already registered',
        status_code=HTTPStatus.CONFLICT,
    )

    # But check that same location different name works
    data = {
        'location': 'kraken',
        'name': 'my_other_kraken',
        'api_key': 'aadddddd',
        'api_secret': 'fffffff'
    }  # noqa: E501
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_simple_ok_response(response)

    # and check that kraken is now registered
    response = requests.get(
        api_url_for(rotkehlchen_api_server, 'exchangesresource'))
    result = assert_proper_response_with_result(response)
    assert result == [
        {
            'location': 'kraken',
            'name': 'my_kraken',
            'kraken_account_type': 'starter'
        },
        {
            'location': 'kraken',
            'name': 'my_other_kraken',
            'kraken_account_type': 'starter'
        },
    ]

    # Check that giving a passphrase is fine
    data = {
        'location': 'coinbasepro',
        'name': 'my_coinbasepro',
        'api_key': 'ddddd',
        'api_secret': 'fffff',
        'passphrase': 'sdf'
    }  # noqa: E501
    with mock_validate_api_key_success(Location.COINBASEPRO):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_simple_ok_response(response)
    # and check that coinbasepro is now registered
    response = requests.get(
        api_url_for(rotkehlchen_api_server, 'exchangesresource'))
    result = assert_proper_response_with_result(response)
    assert result == [
        {
            'location': 'kraken',
            'name': 'my_kraken',
            'kraken_account_type': 'starter'
        },
        {
            'location': 'kraken',
            'name': 'my_other_kraken',
            'kraken_account_type': 'starter'
        },
        {
            'location': 'coinbasepro',
            'name': 'my_coinbasepro'
        },
    ]
Esempio n. 10
0
def test_remove_exchange(rotkehlchen_api_server):
    """Test that removing a setup exchange via the api works"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    db = rotki.data.db
    # Setup coinbase exchange
    data = {
        'location': 'coinbase',
        'name': 'foo',
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }
    with mock_validate_api_key_success(Location.COINBASE):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_simple_ok_response(response)
    # and check it's registered
    response = requests.get(
        api_url_for(rotkehlchen_api_server, 'exchangesresource'))
    result = assert_proper_response_with_result(response)
    assert result == [{'location': 'coinbase', 'name': 'foo'}]

    # Add query ranges to see that they also get deleted when removing the exchange
    cursor = db.conn.cursor()
    cursor.executemany(
        'INSERT OR REPLACE INTO used_query_ranges(name, start_ts, end_ts) VALUES (?, ?, ?)',
        [('coinbasepro_trades', 0, 1579564096),
         ('coinbasepro_margins', 0, 1579564096),
         ('coinbasepro_asset_movements', 0, 1579564096),
         ('coinbase_trades', 0, 1579564096),
         ('coinbase_margins', 0, 1579564096),
         ('coinbase_asset_movements', 0, 1579564096),
         ('binance_trades', 0, 1579564096), ('binance_margins', 0, 1579564096),
         ('binance_asset_movements', 0, 1579564096)],
    )

    # Now remove the registered coinbase exchange
    data = {'location': 'coinbase', 'name': 'foo'}
    response = requests.delete(api_url_for(rotkehlchen_api_server,
                                           'exchangesresource'),
                               json=data)
    assert_simple_ok_response(response)
    # and check that it's not registered anymore
    response = requests.get(
        api_url_for(rotkehlchen_api_server, 'exchangesresource'))
    result = assert_proper_response_with_result(response)
    assert result == []
    # Also check that the coinbase query ranges have been deleted but not the other ones
    cursor = db.conn.cursor()
    result = cursor.execute('SELECT name from used_query_ranges')
    count = 0
    for entry in result:
        count += 1
        msg = 'only binance or coinbasepro query ranges should remain'
        assert 'binance' in entry[0] or 'coinbasepro' in entry[0], msg
    assert count == 6, 'only 6 query ranges should remain in the DB'

    # now try to remove a non-registered exchange
    data = {'location': 'binance', 'name': 'my_binance'}
    response = requests.delete(api_url_for(rotkehlchen_api_server,
                                           "exchangesresource"),
                               json=data)
    assert_error_response(
        response=response,
        contained_in_msg='binance exchange my_binance is not registered',
        status_code=HTTPStatus.CONFLICT,
    )
Esempio n. 11
0
def test_exchange_query_trades(rotkehlchen_api_server_with_exchanges):
    """Test that using the exchange trades query endpoint works fine"""
    async_query = random.choice([False, True])
    server = rotkehlchen_api_server_with_exchanges
    setup = mock_history_processing_and_exchanges(server.rest_api.rotkehlchen)
    # query trades of one specific exchange
    with setup.binance_patch:
        response = requests.get(
            api_url_for(
                server,
                'tradesresource',
            ),
            json={
                'location': 'binance',
                'async_query': async_query
            },
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)
    assert result['entries_found'] > 0
    assert result['entries_limit'] == FREE_TRADES_LIMIT
    assert_binance_trades_result([x['entry'] for x in result['entries']])

    # query trades of all exchanges
    with setup.binance_patch, setup.polo_patch:
        response = requests.get(
            api_url_for(server, 'tradesresource'),
            json={'async_query': async_query},
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    trades = result['entries']
    assert_binance_trades_result([
        x['entry'] for x in trades if x['entry']['location'] == 'binance'
    ])  # noqa: E501
    assert_poloniex_trades_result([
        x['entry'] for x in trades if x['entry']['location'] == 'poloniex'
    ])  # noqa: E501

    def assert_okay(response):
        """Helper function for DRY checking below assertions"""
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(
                rotkehlchen_api_server_with_exchanges, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)
        trades = result['entries']
        assert_binance_trades_result([
            x['entry'] for x in trades if x['entry']['location'] == 'binance'
        ])  # noqa: E501
        assert_poloniex_trades_result(
            trades=[
                x['entry'] for x in trades
                if x['entry']['location'] == 'poloniex'
            ],
            trades_to_check=(2, ),
        )

    # and now query them in a specific time range excluding two of poloniex's trades
    data = {
        'from_timestamp': 1499865548,
        'to_timestamp': 1539713118,
        'async_query': async_query
    }
    with setup.binance_patch, setup.polo_patch:
        response = requests.get(api_url_for(server, "tradesresource"),
                                json=data)
        assert_okay(response)
    # do the same but with query args. This serves as test of from/to timestamp with query args
    with setup.binance_patch, setup.polo_patch:
        response = requests.get(
            api_url_for(server, "tradesresource") + '?' + urlencode(data))
        assert_okay(response)
Esempio n. 12
0
def test_setup_exchange_errors(rotkehlchen_api_server):
    """Test errors and edge cases of setup_exchange endpoint"""

    # Provide unsupported exchange location
    data = {
        'location': 'notexisting',
        'name': 'foo',
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, 'exchangesresource'),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Failed to deserialize Location value notexisting',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Provide invalid type exchange location
    data = {
        'location': 3434,
        'name': 'foo',
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg=
        'Failed to deserialize Location value from non string value: 3434',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Provide invalid type exchange name
    data = {
        'location': 'kraken',
        'name': 55,
        'api_key': 'ddddd',
        'api_secret': 'fffffff'
    }
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Omit exchange name and location
    data = {'api_key': 'ddddd', 'api_secret': 'fffffff'}
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Provide invalid type for api key
    data = {'name': 'kraken', 'api_key': True, 'api_secret': 'fffffff'}
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Given API Key should be a string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Omit api key
    data = {'name': 'kraken', 'api_secret': 'fffffff'}
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Provide invalid type for api secret
    data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 234.1}
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Given API Secret should be a string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Omit api secret
    data = {'name': 'kraken', 'api_key': 'ddddd'}
    with mock_validate_api_key_success(Location.KRAKEN):
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "exchangesresource"),
            json=data,
        )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )
Esempio n. 13
0
def test_edit_exchange_account(rotkehlchen_api_server_with_exchanges):
    server = rotkehlchen_api_server_with_exchanges
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    kraken = try_get_first_exchange(rotki.exchange_manager, Location.KRAKEN)
    poloniex = try_get_first_exchange(rotki.exchange_manager,
                                      Location.POLONIEX)
    assert kraken.name == 'mockkraken'
    assert kraken.account_type == DEFAULT_KRAKEN_ACCOUNT_TYPE
    assert poloniex.name == 'poloniex'

    data = {
        'name': 'mockkraken',
        'location': 'kraken',
        'new_name': 'my_kraken'
    }
    response = requests.patch(api_url_for(server, 'exchangesresource'),
                              json=data)
    result = assert_proper_response_with_result(response)
    assert result is True
    kraken = try_get_first_exchange(rotki.exchange_manager, Location.KRAKEN)
    assert kraken.name == 'my_kraken'
    assert kraken.account_type == DEFAULT_KRAKEN_ACCOUNT_TYPE

    data = {
        'name': 'poloniex',
        'location': 'poloniex',
        'new_name': 'my_poloniex'
    }
    response = requests.patch(api_url_for(server, 'exchangesresource'),
                              json=data)
    result = assert_proper_response_with_result(response)
    assert result is True
    poloniex = try_get_first_exchange(rotki.exchange_manager,
                                      Location.POLONIEX)
    assert poloniex.name == 'my_poloniex'

    # Make sure that existing location exchange but wrong name returns error
    data = {
        'name': 'some_poloniex',
        'location': 'poloniex',
        'new_name': 'other_poloniex'
    }
    response = requests.patch(api_url_for(server, 'exchangesresource'),
                              json=data)
    assert_error_response(
        response=response,
        status_code=HTTPStatus.CONFLICT,
        contained_in_msg=
        'Could not find poloniex exchange some_poloniex for editing',
    )
    # Make sure that real location but not registered returns error
    data = {'name': 'kucoin', 'location': 'kucoin', 'new_name': 'other_kucoin'}
    response = requests.patch(api_url_for(server, 'exchangesresource'),
                              json=data)
    assert_error_response(
        response=response,
        status_code=HTTPStatus.CONFLICT,
        contained_in_msg='Could not find kucoin exchange kucoin for editing',
    )
    # Make sure that not existing location returns error
    data = {
        'name': 'kucoin',
        'location': 'fakeexchange',
        'new_name': 'other_kucoin'
    }
    response = requests.patch(api_url_for(server, 'exchangesresource'),
                              json=data)
    assert_error_response(
        response=response,
        status_code=HTTPStatus.BAD_REQUEST,
        contained_in_msg='Failed to deserialize Location value fakeexchange',
    )
Esempio n. 14
0
def test_query_compound_history(rotkehlchen_api_server, ethereum_accounts):  # pylint: disable=unused-argument  # noqa: E501
    """Check querying the compound history endpoint works. Uses real data"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        eth_balances=['1000000', '2000000', '33000030003', '42323213'],
        token_balances={},
        btc_accounts=None,
        original_queries=['zerion', 'logs', 'blocknobytime'],
    )
    # Since this test can be a bit slow we don't run both async and sync in the same test run
    # Instead we randomly choose one. Eventually both cases will be covered.
    async_query = random.choice([True, False])

    with ExitStack() as stack:
        # patch ethereum/etherscan to not autodetect tokens
        setup.enter_ethereum_patches(stack)
        response = requests.get(api_url_for(
            rotkehlchen_api_server,
            "compoundhistoryresource",
        ),
                                json={'async_query': async_query})
        if async_query:
            task_id = assert_ok_async_response(response)
            # Big timeout since this test can take a long time
            outcome = wait_for_async_task(
                rotkehlchen_api_server,
                task_id,
                timeout=ASYNC_TASK_WAIT_TIMEOUT * 10,
            )
            assert outcome['message'] == ''
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    assert len(result) == 5
    expected_events = process_result_list(EXPECTED_EVENTS)
    # Check only 22 first events, since this is how many there were in the time of
    # the writing of the test. Also don't check events for one of the addresses
    # as it's added later, has many events and it's only to see we handle repay correctly
    to_check_events = [
        x for x in result['events']
        if x['address'] != '0x65304d6aff5096472519ca86a6a1fea31cb47Ced'
    ]
    assert to_check_events[:22] == expected_events
    # Check one repay event
    other_events = [
        x for x in result['events']
        if x['address'] == '0x65304d6aff5096472519ca86a6a1fea31cb47Ced'
    ]
    assert other_events[12]['event_type'] == 'repay'
    expected_hash = '0x48a3e2ef8a746383deac34d74f2f0ea0451b2047701fbed4b9d769a782888eea'
    assert other_events[12]['tx_hash'] == expected_hash
    assert other_events[12]['value']['amount'] == '0.55064402'

    # Check interest profit mappings
    profit_0 = result['interest_profit'][
        '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12']
    assert FVal(profit_0[A_DAI.identifier]['amount']) > FVal(9)
    profit_1 = result['interest_profit'][
        '0xC440f3C87DC4B6843CABc413916220D4f4FeD117']
    assert FVal(profit_1[A_USDC.identifier]['amount']) > FVal(2)
    profit_2 = result['interest_profit'][
        '0xF59D4937BF1305856C3a267bB07791507a3377Ee']
    assert FVal(profit_2[A_DAI.identifier]['amount']) > FVal('0.3')

    # Check debt loss mappings
    debt_0 = result['debt_loss']['0xC440f3C87DC4B6843CABc413916220D4f4FeD117']
    assert FVal(debt_0[A_CUSDC.identifier]['amount']) > FVal('84')
    assert FVal(debt_0['ETH']['amount']) > FVal('0.000012422')

    # Check liquidation profit mappings
    lprofit_0 = result['liquidation_profit'][
        '0xC440f3C87DC4B6843CABc413916220D4f4FeD117']
    assert FVal(lprofit_0['ETH']['amount']) > FVal('0.000012')

    # Check rewards mappings
    rewards_0 = result['rewards']['0xC440f3C87DC4B6843CABc413916220D4f4FeD117']
    assert FVal(rewards_0[A_COMP.identifier]['amount']) > FVal('0.000036')
    rewards_1 = result['rewards']['0xF59D4937BF1305856C3a267bB07791507a3377Ee']
    assert FVal(rewards_1[A_COMP.identifier]['amount']) > FVal('0.003613')
Esempio n. 15
0
def test_add_and_query_ledger_actions(rotkehlchen_api_server,
                                      start_with_valid_premium):
    """Test that querying the ledger actions endpoint works as expected"""
    actions = _add_ledger_actions(rotkehlchen_api_server)

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ), )
    result = assert_proper_response_with_result(response)
    assert result['entries_found'] == 4
    assert result['entries_total'] == 4
    assert result[
        'entries_limit'] == -1 if start_with_valid_premium else FREE_LEDGER_ACTIONS_LIMIT
    assert all(x['ignored_in_accounting'] is False for x in result['entries']
               ), 'by default nothing should be ignored'  # noqa: E501
    result = [x['entry'] for x in result['entries']]
    assert result == actions

    # now let's ignore some ledger actions for accounting
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ignoredactionsresource',
        ),
        json={
            'action_type': 'ledger action',
            'action_ids': ['3', '4']
        },  # external ones
    )
    result = assert_proper_response_with_result(response)
    assert result == {
        'ledger action': [str(a['identifier']) for a in actions[0:2][::-1]]
    }

    # Now filter by location with json body
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={'location': 'external'},
    )
    result = assert_proper_response_with_result(response)
    result = result['entries']
    assert all(x['ignored_in_accounting']
               for x in result), 'all external should be ignored'
    result = [x['entry'] for x in result]
    assert result == actions[0:2]

    # Now filter by location with query param
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ) + '?location=external', )
    result = assert_proper_response_with_result(response)
    result = result['entries']
    assert all(x['ignored_in_accounting']
               for x in result), 'all external should be ignored'
    result = [x['entry'] for x in result]
    assert result == actions[0:2]

    # Now filter by time
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={
            'from_timestamp': 1,
            'to_timestamp': 2
        },
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == actions[2:4]

    # filter by both time and location
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={
            'from_timestamp': 2,
            'to_timestamp': 3,
            'location': 'blockchain'
        },
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[2]]

    # filter by ledger action type
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={'type': 'expense'},
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[2]]

    # filter by asset
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={'asset': 'EUR'},
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[0], actions[1]]

    # filter by asset with timestamp ascending order
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={
            'asset': 'EUR',
            'ascending': True
        },
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[1], actions[0]]

    # test offset/limit pagination
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={
            'offset': 1,
            'limit': 2
        },
    )
    result = assert_proper_response_with_result(response)
    assert result['entries_found'] == 4
    assert result['entries_total'] == 4
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[1], actions[2]]

    # test offset/limit pagination with a filter
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ),
        json={
            'offset': 1,
            'limit': 2,
            'asset': 'EUR'
        },
    )
    result = assert_proper_response_with_result(response)
    assert result['entries_found'] == 2
    assert result['entries_total'] == 4
    result = [x['entry'] for x in result['entries']]
    assert result == [actions[1]]

    def assert_order_by(order_by: str):
        """A helper to keep things DRY in the test"""
        data = {
            'order_by_attribute': order_by,
            'ascending': False,
            'only_cache': True
        }
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                'ledgeractionsresource',
            ),
            json=data,
        )
        result = assert_proper_response_with_result(response)
        assert result[
            'entries_limit'] == -1 if start_with_valid_premium else FREE_LEDGER_ACTIONS_LIMIT  # noqa: E501
        assert result['entries_total'] == 4
        assert result['entries_found'] == 4
        desc_result = result['entries']
        assert len(desc_result) == 4
        data = {
            'order_by_attribute': order_by,
            'ascending': True,
            'only_cache': True
        }
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                'ledgeractionsresource',
            ),
            json=data,
        )
        result = assert_proper_response_with_result(response)
        assert result[
            'entries_limit'] == -1 if start_with_valid_premium else FREE_LEDGER_ACTIONS_LIMIT  # noqa: E501
        assert result['entries_total'] == 4
        assert result['entries_found'] == 4
        asc_result = result['entries']
        assert len(asc_result) == 4
        return desc_result, asc_result

    # test order by location
    desc_result, asc_result = assert_order_by('location')
    assert all(x['entry']['location'] == 'blockchain' for x in desc_result[:2])
    assert all(x['entry']['location'] == 'external' for x in desc_result[2:])
    assert all(x['entry']['location'] == 'external' for x in asc_result[:2])
    assert all(x['entry']['location'] == 'blockchain' for x in asc_result[2:])

    # test order by type
    desc_result, asc_result = assert_order_by('type')
    descending_types = [x['entry']['action_type'] for x in desc_result]
    assert [x['entry']['action_type']
            for x in asc_result] == descending_types[::-1]

    # test order by amount
    desc_result, asc_result = assert_order_by('amount')
    for idx, x in enumerate(desc_result):
        if idx < len(desc_result) - 1:
            assert FVal(x['entry']['amount']) >= FVal(
                desc_result[idx + 1]['entry']['amount'])
    for idx, x in enumerate(asc_result):
        if idx < len(asc_result) - 1:
            assert FVal(x['entry']['amount']) <= FVal(
                asc_result[idx + 1]['entry']['amount'])

    # test order by rate
    desc_result, asc_result = assert_order_by('rate')
    for idx, x in enumerate(desc_result):
        if idx < len(desc_result) - 1:
            this = FVal(
                x['entry']['rate']) if x['entry']['rate'] is not None else ZERO
            next_ = FVal(desc_result[idx + 1]['entry']['rate']) if desc_result[
                idx + 1]['entry']['rate'] is not None else ZERO  # noqa: E501

            assert this >= next_
    for idx, x in enumerate(asc_result):
        if idx < len(asc_result) - 1:
            this = FVal(
                x['entry']['rate']) if x['entry']['rate'] is not None else ZERO
            next_ = FVal(asc_result[idx + 1]['entry']['rate']) if asc_result[
                idx + 1]['entry']['rate'] is not None else ZERO  # noqa: E501
            assert this <= next_
Esempio n. 16
0
def test_query_asset_movements_over_limit(
    rotkehlchen_api_server_with_exchanges,
    start_with_valid_premium,
):
    """Test that using the asset movements query endpoint works fine"""
    start_ts = 0
    end_ts = 1598453214
    server = rotkehlchen_api_server_with_exchanges
    rotki = server.rest_api.rotkehlchen
    # Make sure online kraken is not queried by setting query ranges
    rotki.data.db.update_used_query_range(
        name='kraken_asset_movements',
        start_ts=start_ts,
        end_ts=end_ts,
    )
    polo_entries_num = 4
    # Set a ton of kraken asset movements in the DB
    kraken_entries_num = FREE_ASSET_MOVEMENTS_LIMIT + 50
    movements = [
        AssetMovement(location=Location.KRAKEN,
                      category=AssetMovementCategory.DEPOSIT,
                      address=None,
                      transaction_id=None,
                      timestamp=x,
                      asset=A_BTC,
                      amount=FVal(x * 100),
                      fee_asset=A_BTC,
                      fee=FVal(x),
                      link='') for x in range(kraken_entries_num)
    ]
    rotki.data.db.add_asset_movements(movements)
    all_movements_num = kraken_entries_num + polo_entries_num
    setup = prepare_rotki_for_history_processing_test(
        server.rest_api.rotkehlchen)

    # Check that querying movements with/without limits works even if we query two times
    for _ in range(2):
        # query asset movements of polo which has less movements than the limit
        with setup.polo_patch:
            response = requests.get(
                api_url_for(
                    server,
                    'assetmovementsresource',
                ),
                json={'location': 'poloniex'},
            )
        result = assert_proper_response_with_result(response)
        assert result['entries_found'] == all_movements_num
        assert result[
            'entries_limit'] == -1 if start_with_valid_premium else FREE_ASSET_MOVEMENTS_LIMIT  # noqa: E501
        assert_poloniex_asset_movements(
            [x['entry'] for x in result['entries']], deserialized=True)

        # now query kraken which has a ton of DB entries
        response = requests.get(
            api_url_for(server, "assetmovementsresource"),
            json={'location': 'kraken'},
        )
        result = assert_proper_response_with_result(response)

        if start_with_valid_premium:
            assert len(result['entries']) == kraken_entries_num
            assert result['entries_limit'] == -1
            assert result['entries_found'] == all_movements_num
        else:
            assert len(result['entries']
                       ) == FREE_ASSET_MOVEMENTS_LIMIT - polo_entries_num
            assert result['entries_limit'] == FREE_ASSET_MOVEMENTS_LIMIT
            assert result['entries_found'] == all_movements_num
Esempio n. 17
0
def test_query_statistics_asset_balance_errors(rotkehlchen_api_server,
                                               rest_api_port):
    """Test that errors at the statistics asset balance over time endpoint are hanled properly"""
    start_time = ts_now()

    # Check that no asset given is an error
    response = requests.get(
        f'http://localhost:{rest_api_port}/api/1/statistics/balance')
    assert_error_response(
        response=response,
        status_code=HTTPStatus.NOT_FOUND,
    )

    # Check that an invalid asset given is an error
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "statisticsassetbalanceresource",
            asset="NOTAREALASSSETLOL",
        ),
        json={
            'from_timestamp': 0,
            'to_timestamp': start_time
        },
    )
    assert_error_response(
        response=response,
        contained_in_msg='Unknown asset NOTAREALASSSETLOL provided',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Check that giving invalid value for from_timestamp is an error
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "statisticsassetbalanceresource",
            asset="BTC",
        ),
        json={
            'from_timestamp': 'dsad',
            'to_timestamp': start_time
        },
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        'Failed to deserialize a timestamp entry from string dsad',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Check that giving invalid value for to_timestamp is an error
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "statisticsassetbalanceresource",
            asset="BTC",
        ),
        json={
            'from_timestamp': 0,
            'to_timestamp': 53434.32
        },
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        '"Failed to deserialize a timestamp entry. Unexpected type',
        status_code=HTTPStatus.BAD_REQUEST,
    )
Esempio n. 18
0
def test_set_settings_errors(rotkehlchen_api_server):
    """set settings errors and edge cases test"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    # set timeout to 1 second to timeout faster
    rotki.blockchain.ethchain.eth_rpc_timeout = 1

    # Eth rpc endpoint to which we can't connect
    data = {
        'eth_rpc_endpoint': 'http://lol.com:5555',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Failed to connect to ethereum node at endpoint',
        status_code=HTTPStatus.CONFLICT,
    )

    # Invalid type for eth_rpc_endpoint
    data = {
        'eth_rpc_endpoint': 5555,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for premium_should_sync
    data = {
        'premium_should_sync': 444,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid boolean',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for include_crypto2crypto
    data = {
        'include_crypto2crypto': 'ffdsdasd',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid boolean',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for anonymized_logs
    data = {
        'anonymized_logs': 555.1,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid boolean',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid range for ui_floating_precision
    data = {
        'ui_floating_precision': -1,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Floating numbers precision in the UI must be between 0 and 8',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    data = {
        'ui_floating_precision': 9,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Floating numbers precision in the UI must be between 0 and 8',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for ui_floating_precision
    data = {
        'ui_floating_precision': 'dasdsds',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid integer',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid range for taxfree_after_period
    data = {
        'taxfree_after_period': -2,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Number of seconds after which taxfree period starts should not be negat',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for taxfree_after_period
    data = {
        'taxfree_after_period': 'dsad',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid integer',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid range for balance_save_frequency
    data = {
        'balance_save_frequency': 0,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='The number of hours after which balances should be saved should be >= 1',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid range for balance_save_frequency
    data = {
        'balance_save_frequency': 'dasdsd',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid integer',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for include_gas_cost
    data = {
        'include_gas_costs': 55.1,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid boolean',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid type for historical_data_start
    data = {
        'historical_data_start': 12,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # Invalid asset for main currenty
    data = {
        'main_currency': 'DSDSDSAD',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Unknown asset DSDSDSAD',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # non FIAT asset for main currency
    data = {
        'main_currency': 'ETH',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Asset ETH is not a FIAT asset',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # invalid type main currency
    data = {
        'main_currency': 243243,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Tried to initialize an asset out of a non-string identifier',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # invalid type date_display_format
    data = {
        'date_display_format': 124.1,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server, "settingsresource"), json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )
Esempio n. 19
0
def test_query_statistics_asset_balance(
    rotkehlchen_api_server_with_exchanges,
    ethereum_accounts,
    btc_accounts,
    start_with_valid_premium,
):
    """Test that using the statistics asset balance over time 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
    setup = setup_balances(rotki, ethereum_accounts, btc_accounts)

    # 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)

    # and now test that statistics work fine for ETH, with default time range (0 - now)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsassetbalanceresource",
            asset="ETH",
        ), )
    if start_with_valid_premium:
        result = assert_proper_response_with_result(response)
        assert len(result) == 1
        entry = result[0]
        assert len(entry) == 4
        assert FVal(entry['amount']) == get_asset_balance_total(A_ETH, setup)
        assert entry['category'] == 'asset'
        assert entry['time'] >= start_time
        assert entry['usd_value'] is not None
    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 BTC, with given time range
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsassetbalanceresource",
            asset="BTC",
        ),
        json={
            'from_timestamp': 0,
            'to_timestamp': start_time + 60000
        },
    )
    if start_with_valid_premium:
        result = assert_proper_response_with_result(response)
        assert len(result) == 1
        entry = result[0]
        assert len(entry) == 4
        assert FVal(entry['amount']) == get_asset_balance_total(A_BTC, setup)
        assert entry['time'] >= start_time
        assert entry['category'] == 'asset'
        assert entry['usd_value'] is not None
    else:
        assert_error_response(
            response=response,
            contained_in_msg=
            'logged in user testuser does not have a premium subscription',
            status_code=HTTPStatus.CONFLICT,
        )

    # finally test that if the time range is not including the saved balances we get nothing back
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            "statisticsassetbalanceresource",
            asset="BTC",
        ),
        json={
            'from_timestamp': 0,
            'to_timestamp': start_time - 1
        },
    )
    if start_with_valid_premium:
        result = assert_proper_response_with_result(response)
        assert len(result) == 0
    else:
        assert_error_response(
            response=response,
            contained_in_msg=
            'logged in user testuser does not have a premium subscription',
            status_code=HTTPStatus.CONFLICT,
        )
Esempio n. 20
0
def test_set_settings(rotkehlchen_api_server):
    """Happy case settings modification test"""
    # Get the starting settings
    response = requests.get(api_url_for(rotkehlchen_api_server, "settingsresource"))
    assert_proper_response(response)
    json_data = response.json()
    original_settings = json_data['result']
    assert json_data['message'] == ''
    # Create new settings which modify all of the original ones
    new_settings = {}
    unmodifiable_settings = (
        'version',
        'last_write_ts',
        'last_data_upload_ts',
        'last_balance_save',
    )
    for setting, value in original_settings.items():
        if setting in unmodifiable_settings:
            continue
        elif setting == 'historical_data_start':
            value = '10/10/2016'
        elif setting == 'date_display_format':
            value = '%d/%m/%Y-%H:%M:%S'
        elif setting == 'eth_rpc_endpoint':
            value = 'http://working.nodes.com:8545'
        elif setting == 'main_currency':
            value = 'JPY'
        elif type(value) == bool:
            value = not value
        elif type(value) == int:
            value += 1
        else:
            raise AssertionError(f'Unexpected settting {setting} encountered')

        new_settings[setting] = value

    # modify the settings
    block_query = patch('rotkehlchen.ethchain.Ethchain.query_eth_highest_block', return_value=0)
    mock_web3 = patch('rotkehlchen.ethchain.Web3', MockWeb3)
    with block_query, mock_web3:
        response = requests.put(
            api_url_for(rotkehlchen_api_server, "settingsresource"),
            json=new_settings,
        )
    # Check that new settings are returned in the response
    assert_proper_response(response)
    json_data = response.json()
    assert json_data['message'] == ''
    result = json_data['result']
    assert result['version'] == ROTKEHLCHEN_DB_VERSION
    for setting, value in new_settings.items():
        msg = f'Error for {setting} setting. Expected: {value}. Got: {result[setting]}'
        assert result[setting] == value, msg

    # now check that the same settings are returned in a settings query
    response = requests.get(api_url_for(rotkehlchen_api_server, "settingsresource"))
    assert_proper_response(response)
    json_data = response.json()
    result = json_data['result']
    assert json_data['message'] == ''
    for setting, value in new_settings.items():
        assert result[setting] == value
Esempio n. 21
0
def test_query_vaults_wbtc(rotkehlchen_api_server, ethereum_accounts):
    """Check vault info and details for a vault with WBTC as collateral"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    proxies_mapping = {
        ethereum_accounts[0]:
        '0x9684e6C1c7B79868839b27F88bA6d5A176367075',  # 8913
    }

    mock_proxies(rotki, proxies_mapping)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultsresource",
        ))
    # That proxy has 3 vaults. We only want to test 8913, which is closed/repaid so just keep that
    vaults = [
        x for x in assert_proper_response_with_result(response)
        if x['identifier'] == 8913
    ]
    vault_8913 = MakerDAOVault(
        identifier=8913,
        owner=ethereum_accounts[0],
        collateral_type='WBTC-A',
        urn='0x37f7B3C82A9Edc13FdCcE66E7d500b3698A13294',
        collateral_asset=A_WBTC,
        collateral_amount=ZERO,
        collateral_usd_value=ZERO,
        debt_value=ZERO,
        collateralization_ratio=None,
        liquidation_ratio=FVal(1.5),
        liquidation_price=None,
        stability_fee=FVal(0.01),
    )
    expected_vaults = [vault_8913.serialize()]
    assert_serialized_lists_equal(expected_vaults, vaults)
    # And also make sure that the internal mapping will only query details of 8913
    rotki.chain_manager.makerdao.vault_mappings = {
        ethereum_accounts[0]: [vault_8913]
    }

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultdetailsresource",
        ))
    vault_8913_details = {
        'identifier':
        8913,
        'creation_ts':
        1588664698,
        'total_interest_owed':
        FVal('0.1903819198'),
        'total_liquidated_amount':
        ZERO,
        'total_liquidated_usd':
        ZERO,
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('0.011'),
            'amount_usd_value':
            FVal('87.06599'),
            'timestamp':
            1588664698,
            'tx_hash':
            '0x9ba4a6187fa2c49ba327e7c923846a08a1e972017ec41d3f9f66ef524f7dde59',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('25'),
            'amount_usd_value':
            FVal('25.15'),
            'timestamp':
            1588664698,
            'tx_hash':
            '0x9ba4a6187fa2c49ba327e7c923846a08a1e972017ec41d3f9f66ef524f7dde59',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('25.000248996'),
            'amount_usd_value':
            FVal('25.15025'),
            'timestamp':
            1588696496,
            'tx_hash':
            '0x8bd960e7eb8b9e2b81d2446d1844dd63f94636c7800ea5e3b4d926ea0244c66c',
        }, {
            'event_type':
            'deposit',
            'amount':
            FVal('0.0113'),
            'amount_usd_value':
            FVal('89.440517'),
            'timestamp':
            1588720248,
            'tx_hash':
            '0x678c4da562173c102473f1904ff293a767ebac9ec6c7d728ef2fd41acf00a13a',
        }],  # way too many events in the vault, so no need to check them all
    }
    details = assert_proper_response_with_result(response)
    assert len(details) == 1
    assert_serialized_dicts_equal(
        details[0],
        vault_8913_details,
        # Checking only the first 4 events
        length_list_keymap={'events': 4},
    )
Esempio n. 22
0
def test_add_and_query_ledger_actions(rotkehlchen_api_server):
    """Test that querying the ledger actions endpoint works as expected"""
    actions = _add_ledger_actions(rotkehlchen_api_server)

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ledgeractionsresource',
        ), )
    result = assert_proper_response_with_result(response)
    assert result['entries_found'] == 4
    assert result['entries_limit'] == FREE_LEDGER_ACTIONS_LIMIT
    assert all(x['ignored_in_accounting'] is False for x in result['entries']
               ), 'by default nothing should be ignored'  # noqa: E501
    result = [x['entry'] for x in result['entries']]
    assert result == actions

    # now let's ignore some ledger actions for accounting
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ignoredactionsresource',
        ),
        json={
            'action_type': 'ledger action',
            'action_ids': ['3', '4']
        },  # external ones
    )
    result = assert_proper_response_with_result(response)
    assert result == {
        'ledger action': [str(a['identifier']) for a in actions[2:]]
    }

    # Now filter by location with json body
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "ledgeractionsresource",
        ),
        json={'location': 'external'},
    )
    result = assert_proper_response_with_result(response)
    result = result['entries']
    assert all(x['ignored_in_accounting']
               for x in result), 'all external should be ignored'
    result = [x['entry'] for x in result]
    assert result == actions[2:]

    # Now filter by location with query param
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "ledgeractionsresource",
        ) + '?location=external', )
    result = assert_proper_response_with_result(response)
    result = result['entries']
    assert all(x['ignored_in_accounting']
               for x in result), 'all external should be ignored'
    result = [x['entry'] for x in result]
    assert result == actions[2:]

    # Now filter by time
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "ledgeractionsresource",
        ),
        json={
            'from_timestamp': 1,
            'to_timestamp': 2
        },
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == actions[:2]

    # and finally filter by both time and location
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "ledgeractionsresource",
        ),
        json={
            'from_timestamp': 2,
            'to_timestamp': 3,
            'location': 'blockchain'
        },
    )
    result = assert_proper_response_with_result(response)
    result = [x['entry'] for x in result['entries']]
    assert result == actions[1:2]
Esempio n. 23
0
def test_two_vaults_same_account_same_collateral(rotkehlchen_api_server,
                                                 ethereum_accounts):
    """Check that no events are duplicated between vaults for same collateral by same account

    Test for vaults side of https://github.com/rotki/rotki/issues/1032
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    proxies_mapping = {
        # proxy for 8632 and 8543
        ethereum_accounts[0]:
        '0xAe9996b76bdAa003ace6D66328A6942565f5768d',
    }
    mock_proxies(rotki, proxies_mapping)

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultsresource",
        ))
    vaults = assert_proper_response_with_result(response)
    vault_8543 = {
        'identifier': 8543,
        'owner': ethereum_accounts[0],
        'collateral_type': 'ETH-A',
        'collateral_asset': 'ETH',
        'collateral_amount': ZERO,
        'collateral_usd_value': ZERO,
        'debt_value': ZERO,
        'collateralization_ratio': None,
        'liquidation_ratio': '150.00%',
        'liquidation_price': None,
        'stability_fee': '0.00%',
    }
    vault_8632 = {
        'identifier': 8632,
        'owner': ethereum_accounts[0],
        'collateral_type': 'ETH-A',
        'collateral_asset': 'ETH',
        'collateral_amount': '0.0',
        'collateral_usd_value': '0.0',
        'debt_value': '0.0',
        'collateralization_ratio': None,
        'liquidation_ratio': '150.00%',
        'liquidation_price': None,
        'stability_fee': '0.00%',
    }
    assert len(vaults) == 2
    assert_serialized_dicts_equal(vaults[0], vault_8543)
    assert_serialized_dicts_equal(vaults[1],
                                  vault_8632,
                                  ignore_keys=VAULT_IGNORE_KEYS)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "makerdaovaultdetailsresource",
        ))
    vault_8543_details = {
        'identifier':
        8543,
        'creation_ts':
        1587910979,
        'total_interest_owed':
        ZERO,
        'total_liquidated_amount':
        ZERO,
        'total_liquidated_usd':
        ZERO,
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('1'),
            'amount_usd_value':
            FVal('197.78'),
            'timestamp':
            1587910979,
            'tx_hash':
            '0xf59858df4e42cdc2aecfebdcf38e1df841866c6a9eb3adb6bde9a844564a3bb6',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('80'),
            'amount_usd_value':
            FVal('81.2'),
            'timestamp':
            1587910979,
            'tx_hash':
            '0xf59858df4e42cdc2aecfebdcf38e1df841866c6a9eb3adb6bde9a844564a3bb6',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('80'),
            'amount_usd_value':
            FVal('80.24'),
            'timestamp':
            1589989097,
            'tx_hash':
            '0x52396f7d20db54e2e9e716698b643a39815ff149a6cccbe9c7597dc9e06bb9d3',
        }, {
            'event_type':
            'deposit',
            'amount':
            FVal('3.5'),
            'amount_usd_value':
            FVal('734.475'),
            'timestamp':
            1589993538,
            'tx_hash':
            '0x3c3942dc40fe68303098d91e765ceecaed4664bba0ef8f8e684b6f0e61968c6c',
        }, {
            'event_type':
            'withdraw',
            'amount':
            FVal('4.5'),
            'amount_usd_value':
            FVal('893.52'),
            'timestamp':
            1590043499,
            'tx_hash':
            '0xbcd4158f0089404f6ab5378517762cddc13d21c9d2fcf3fd45cf1cf4b656242c',
        }],
    }
    vault_8632_details = {
        'identifier':
        8632,
        'creation_ts':
        1588174425,
        'total_interest_owed':
        ZERO,
        'total_liquidated_amount':
        ZERO,
        'total_liquidated_usd':
        ZERO,
        'events': [{
            'event_type':
            'deposit',
            'amount':
            FVal('2.4'),
            'amount_usd_value':
            FVal('517.32'),
            'timestamp':
            1588174425,
            'tx_hash':
            '0xdb677a4257b5bdb305c278102d7b2460408bb7a3981414b994f4dd80a737ac2a',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('192'),
            'amount_usd_value':
            FVal('194.688'),
            'timestamp':
            1588174425,
            'tx_hash':
            '0xdb677a4257b5bdb305c278102d7b2460408bb7a3981414b994f4dd80a737ac2a',
        }, {
            'event_type':
            'payback',
            'amount':
            FVal('192'),
            'amount_usd_value':
            FVal('192.192'),
            'timestamp':
            1590042891,
            'tx_hash':
            '0x488a937677030cc810d0062001c08c944ecf6329b24a45ae9480bada8147bf75',
        }, {
            'event_type':
            'deposit',
            'amount':
            FVal('4.4'),
            'amount_usd_value':
            FVal('873.664'),
            'timestamp':
            1590043699,
            'tx_hash':
            '0x712ddb654b878bcb30c5344d7c18f7f796fe94abd6e5b8a22b2da0a6c99bb425',
        }, {
            'event_type':
            'generate',
            'amount':
            FVal('429.79'),
            'amount_usd_value':
            FVal('430.21979'),
            'timestamp':
            1590044118,
            'tx_hash':
            '0x36bfa27e157c03393a8816f6c1e3e990474f8f7473413810d87e2f4981d58044',
        }],
    }
    details = assert_proper_response_with_result(response)
    assert len(details) == 2
    assert_serialized_dicts_equal(details[0], vault_8543_details)
    assert_serialized_dicts_equal(
        details[1],
        vault_8632_details,
        ignore_keys=[
            'total_interest_owed', 'total_liquidated_amount',
            'total_liquidated_usd'
        ],
        # Checking only the first 5 events, since that's how many we had when the test was written
        length_list_keymap={'events': 5},
    )
Esempio n. 24
0
def test_add_and_query_manually_tracked_balances(
        rotkehlchen_api_server,
        ethereum_accounts,
):
    """Test that adding and querying manually tracked balances via the API works fine"""
    async_query = random.choice([False, True])
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    setup = setup_balances(rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None)
    _populate_tags(rotkehlchen_api_server)
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'async_query': async_query},
    )
    if async_query:
        task_id = assert_ok_async_response(response)
        outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
        result = outcome['result']
    else:
        result = assert_proper_response_with_result(response)

    assert result['balances'] == [], 'In the beginning we should have no entries'

    balances = _populate_initial_balances(rotkehlchen_api_server)

    # now query and make sure the added balances are returned
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'async_query': async_query},
    )
    if async_query:
        task_id = assert_ok_async_response(response)
        outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
        result = outcome['result']
    else:
        result = assert_proper_response_with_result(response)
    assert_balances_match(expected_balances=balances, returned_balances=result['balances'])

    now = ts_now()
    # Also now test for https://github.com/rotki/rotki/issues/942 by querying for all balances
    # causing all balances to be saved and making sure the manual balances also got saved
    with ExitStack() as stack:
        setup.enter_ethereum_patches(stack)
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                "allbalancesresource",
            ), json={'async_query': async_query},
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    result = result['assets']
    assert result['BTC']['amount'] == '1.425'
    assert result['XMR']['amount'] == '50.315'
    assert result[A_BNB.identifier]['amount'] == '155'
    # Check DB to make sure a save happened
    assert rotki.data.db.get_last_balance_save_time() >= now
    assert set(rotki.data.db.query_owned_assets()) == {
        'BTC',
        'XMR',
        A_BNB.identifier,
        'ETH',
        A_RDN.identifier,
    }
Esempio n. 25
0
def test_get_events(
        rotkehlchen_api_server,
        ethereum_accounts,  # pylint: disable=unused-argument
        rotki_premium_credentials,  # pylint: disable=unused-argument
        start_with_valid_premium,  # pylint: disable=unused-argument
):
    async_query = random.choice([False, True])
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen

    # Set module premium is required for calling `get_balances()`
    premium = None
    if start_with_valid_premium:
        premium = Premium(rotki_premium_credentials)

    rotki.chain_manager.adex.premium = premium

    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=None,
        original_queries=['zerion', 'logs', 'blocknobytime'],
    )

    with ExitStack() as stack:
        # patch ethereum/etherscan to not autodetect tokens
        setup.enter_ethereum_patches(stack)
        response = requests.get(
            api_url_for(rotkehlchen_api_server, 'adexhistoryresource'),
            json={
                'async_query': async_query,
                'to_timestamp': 1611747322
            },
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
            assert outcome['message'] == ''
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    identity_address = '0x2a6c38D16BFdc7b4a20f1F982c058F07BDCe9204'
    tom_pool_id = '0x2ce0c96383fb229d9776f33846e983a956a7d95844fac57b180ed0071d93bb28'
    bond_id = '0x540cab9883923c01e657d5da4ca5674b6e4626b4a148224635495502d674c7c5'
    result = result[ADEX_TEST_ADDR]
    expected_events = [
        Bond(
            tx_hash=
            '0x9989f47c6c0a761f98f910ac24e2438d858be96c12124a13be4bb4b3150c55ea',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1604366004,
            bond_id=bond_id,
            pool_id=tom_pool_id,
            value=Balance(FVal(100000), FVal(200000)),
            nonce=0,
            slashed_at=0,
        ),
        ChannelWithdraw(
            tx_hash=
            '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1607453764,
            channel_id='',
            pool_id=tom_pool_id,
            value=Balance(FVal('5056.894263641728544592'),
                          FVal('10113.788527283457089184')),
            token=A_ADX,
        ),
        Unbond(
            tx_hash=
            '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1607453764,
            bond_id=bond_id,
            pool_id=tom_pool_id,
            value=Balance(FVal(100000), FVal(200000)),
        )
    ]
    assert len(result['events']) == 8
    assert result['events'][:len(expected_events)] == [
        x.serialize() for x in expected_events
    ]
    assert 'staking_details' in result
    # Make sure events end up in the DB
    assert len(rotki.data.db.get_adex_events()) != 0
    # test adex data purging from the db works
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'namedethereummoduledataresource',
            module_name='adex',
        ))
    assert_simple_ok_response(response)
    assert len(rotki.data.db.get_adex_events()) == 0
Esempio n. 26
0
def test_add_edit_manually_tracked_balances_errors(
        rotkehlchen_api_server,
        verb,
):
    """Test that errors in input data while adding/editing manually tracked balances
    are handled properly"""
    _populate_tags(rotkehlchen_api_server)
    balances = {'balances': [{
        "asset": "XMR",
        "label": "My monero wallet",
        "amount": "50.315",
        "tags": ["public", "mInEr"],
        "location": "blockchain",
    }, {
        "asset": "BTC",
        "label": "My XPUB BTC wallet",
        "amount": "1.425",
        "location": "blockchain",
    }]}

    # invalid initial input type
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            'manuallytrackedbalancesresource',
        ), json=[1, 2, 3],
    )
    assert_error_response(
        response=response,
        contained_in_msg='Invalid input type',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing balances
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'foo': 1},
    )
    assert_error_response(
        response=response,
        contained_in_msg='balances": ["Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong type for balances
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'balances': 'foo'},
    )
    assert_error_response(
        response=response,
        contained_in_msg='balances": ["Not a valid list',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing asset entry
    data = deepcopy(balances)
    del data['balances'][0]['asset']
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # invalid type for asset
    data = deepcopy(balances)
    data['balances'][0]['asset'] = 123
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to initialize an asset out of a non-string identifier',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # unknown asset
    data = deepcopy(balances)
    data['balances'][0]['asset'] = 'SDSFFGFA'
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Unknown asset SDSFFGFA provided',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing label entry
    data = deepcopy(balances)
    del data['balances'][0]['label']
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong type for label
    data = deepcopy(balances)
    data['balances'][0]['label'] = 55
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='label": ["Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing amount entry
    data = deepcopy(balances)
    del data['balances'][0]['amount']
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # wrong type for amount
    data = deepcopy(balances)
    data['balances'][0]['amount'] = 'gra'
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Failed to deserialize an amount entry',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing location entry
    data = deepcopy(balances)
    del data['balances'][0]['location']
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # wrong type for location
    data = deepcopy(balances)
    data['balances'][0]['location'] = 55
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            'manuallytrackedbalancesresource',
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Failed to deserialize Location value from non string value',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # invalid location
    data = deepcopy(balances)
    data['balances'][0]['location'] = 'foo'
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            'manuallytrackedbalancesresource',
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Failed to deserialize Location value foo',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong type for tags
    data = deepcopy(balances)
    data['balances'][0]['tags'] = 55
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            'manuallytrackedbalancesresource',
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='tags": ["Not a valid list',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # wrong type in list of tags
    data = deepcopy(balances)
    data['balances'][0]['tags'] = ['foo', 55]
    response = requests.request(
        verb,
        api_url_for(
            rotkehlchen_api_server,
            'manuallytrackedbalancesresource',
        ), json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='"tags": {"1": ["Not a valid string."',
        status_code=HTTPStatus.BAD_REQUEST,
    )
Esempio n. 27
0
def test_user_creation_errors(rotkehlchen_api_server, data_dir):
    """Test errors and edge cases for user creation"""
    # Missing username
    username = '******'
    data = {
        'password': '******',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
    )
    # Missing password
    username = '******'
    data = {
        'name': username,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
    )

    # Invalid type for name
    data = {
        'name': 5435345.31,
        'password': '******',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
    )

    # Invalid type for password
    data = {
        'name': username,
        'password': 4535,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
    )

    # Provide only premium_api_key
    data = {
        'name': username,
        'password': '******',
        'premium_api_key': 'asdsada',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Must provide both or neither of api key/secret',
    )
    # Provide only premium_api_secret
    data = {
        'name': username,
        'password': '******',
        'premium_api_secret': 'asdsada',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Must provide both or neither of api key/secret',
    )
    # Invalid type for premium api key
    data = {
        'name': username,
        'password': '******',
        'premium_api_key': True,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
    )
    # Invalid type for premium api secret
    data = {
        'name': username,
        'password': '******',
        'premium_api_secret': 45.2,
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
    )

    # Check that the directory was NOT created
    assert not Path(data_dir / username / 'rotkehlchen.db').exists()

    # Let's pretend there is another user, and try to create them again
    Path(data_dir / 'another_user').mkdir()
    Path(data_dir / 'another_user' / 'rotkehlchen.db').touch()
    data = {
        'name': 'another_user',
        'password': '******',
    }
    response = requests.put(api_url_for(rotkehlchen_api_server,
                                        "usersresource"),
                            json=data)
    assert_error_response(
        response=response,
        contained_in_msg='User another_user already exists',
        status_code=HTTPStatus.CONFLICT,
    )
Esempio n. 28
0
def test_delete_manually_tracked_balances_errors(rotkehlchen_api_server):
    """Test that errors at deleting manually tracked balances in the API are handled"""
    _populate_tags(rotkehlchen_api_server)
    _populate_initial_balances(rotkehlchen_api_server)

    # invalid initial input type
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json=[],
    )
    assert_error_response(
        response=response,
        contained_in_msg="Invalid input type",
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # missing labels
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={},
    )
    assert_error_response(
        response=response,
        contained_in_msg='labels": ["Missing data for required field',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong type for labels
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'labels': 1},
    )
    assert_error_response(
        response=response,
        contained_in_msg='"labels": ["Not a valid list',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # wrong type for label entries
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'labels': ['My monero wallet', 55]},
    )
    assert_error_response(
        response=response,
        contained_in_msg='"labels": {"1": ["Not a valid string."',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # delete non-existing label
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            "manuallytrackedbalancesresource",
        ), json={'labels': ['My monero wallet', 'nonexisting']},
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to remove 1 manually tracked balance labels that do not exist',
        status_code=HTTPStatus.BAD_REQUEST,
    )
Esempio n. 29
0
def test_user_login(rotkehlchen_api_server, username, db_password, data_dir):
    """Test that user login works properly"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen

    # Let's pretend there is another user, and try to create them again
    Path(data_dir / 'another_user').mkdir()
    Path(data_dir / 'another_user' / 'rotkehlchen.db').touch()

    # Check users status
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedin'
    assert users_data['another_user'] == 'loggedout'

    # Logout of the active user
    data = {'action': 'logout'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_simple_ok_response(response)
    assert rotki.user_is_logged_in is False
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedout'
    assert users_data['another_user'] == 'loggedout'

    # Now let's try to login
    data = {
        'action': 'login',
        "password": db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    # And make sure it works
    assert_proper_response(response)
    check_proper_unlock_result(response.json())
    assert rotki.user_is_logged_in is True
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedin'
    assert users_data['another_user'] == 'loggedout'

    # Logout again
    data = {'action': 'logout'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_simple_ok_response(response)
    assert rotki.user_is_logged_in is False
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedout'
    assert users_data['another_user'] == 'loggedout'

    # Now try to login with a wrong password
    data = {
        'action': 'login',
        "password": '******',
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    # And make sure it fails
    assert_error_response(
        response=response,
        contained_in_msg='Wrong password or invalid/corrupt database for user',
        status_code=HTTPStatus.UNAUTHORIZED,
    )
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedout'
    assert users_data['another_user'] == 'loggedout'

    # Now let's manually add valid but not authenticable premium credentials in the DB
    data = {
        'action': 'login',
        "password": db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    credentials = PremiumCredentials(VALID_PREMIUM_KEY, VALID_PREMIUM_SECRET)
    rotki.data.db.set_rotkehlchen_premium(credentials)
    data = {'action': 'logout'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_simple_ok_response(response)
    assert rotki.user_is_logged_in is False
    # And try to login while having these unauthenticable premium credentials in the DB
    data = {
        'action': 'login',
        "password": db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    # And make sure it works despite having unauthenticable premium credentials in the DB
    assert_proper_response(response)
    check_proper_unlock_result(response.json())
    assert rotki.user_is_logged_in is True
    users_data = check_user_status(rotkehlchen_api_server)
    assert len(users_data) == 2
    assert users_data[username] == 'loggedin'
    assert users_data['another_user'] == 'loggedout'
Esempio n. 30
0
def test_users_by_name_endpoint_errors(rotkehlchen_api_server, username,
                                       db_password):
    """Test that user by name endpoint errors are handled (for login/logout and edit)"""
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    # Now let's try to login while the user is already logged in
    data = {
        'action': 'login',
        'password': db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    expected_msg = (
        f'Can not login to user {username} because user {username} is '
        f'already logged in. Log out of that user first')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )
    assert rotki.user_is_logged_in is True

    # Logout of the active user
    data = {'action': 'logout'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_simple_ok_response(response)
    assert rotki.user_is_logged_in is False

    # Now let's try to login with an invalid password
    data = {
        'action': 'login',
        'password': '******',
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Wrong password or invalid/corrupt database for user',
        status_code=HTTPStatus.UNAUTHORIZED,
    )
    assert rotki.user_is_logged_in is False

    # Login action without a password
    data = {'action': 'login', 'sync_approval': 'unknown'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing password field for login',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # No action and no premium credentials
    data = {'sync_approval': 'unknown'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        'Without an action premium api key and secret must be provided',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # No action and only premium key
    data = {'sync_approval': 'unknown', 'premium_api_key': VALID_PREMIUM_KEY}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        'Without an action premium api key and secret must be provided',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # No action and only premium secret
    data = {
        'sync_approval': 'unknown',
        'premium_api_secret': VALID_PREMIUM_SECRET
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        'Without an action premium api key and secret must be provided',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # Invalid action type
    data = {
        'action': 555.3,
        'password': db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # Invalid action string
    data = {
        'action': 'chopwood',
        'password': db_password,
        'sync_approval': 'unknown'
    }
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Must be one of: login, logout',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False

    # Invalid password type
    data = {'action': 'login', 'password': True, 'sync_approval': 'unknown'}
    response = requests.patch(
        api_url_for(rotkehlchen_api_server,
                    "usersbynameresource",
                    name=username),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
        status_code=HTTPStatus.BAD_REQUEST,
    )
    assert rotki.user_is_logged_in is False