Exemplo n.º 1
0
    def __init__(
            self,
            blockchain_accounts,
            all_eth_tokens,
            owned_eth_tokens,
            inquirer,
            ethrpc_port
    ):
        self.lock = Semaphore()
        self.results_cache = {}
        self.ethchain = Ethchain(ethrpc_port)
        self.inquirer = inquirer

        self.accounts = blockchain_accounts
        self.owned_eth_tokens = owned_eth_tokens

        # All the known tokens, along with addresses and decimals
        self.all_eth_tokens = {}
        for token in all_eth_tokens:
            try:
                token_symbol = str(token['symbol'])
            except (UnicodeDecodeError, UnicodeEncodeError):
                # skip tokens with problems in unicode encoding decoding
                continue

            self.all_eth_tokens[token_symbol] = {
                'address': token['address'],
                'decimal': token['decimal']
            }
        # Per account balances
        self.balances = defaultdict(dict)
        # Per asset total balances
        self.totals = defaultdict(dict)
Exemplo n.º 2
0
    def unlock_user(self, user, password, create_new, sync_approval, api_key,
                    api_secret):
        log.info(
            'Unlocking user',
            user=user,
            create_new=create_new,
            sync_approval=sync_approval,
        )
        # unlock or create the DB
        self.password = password
        self.user_directory = self.data.unlock(user, password, create_new)
        self.try_premium_at_start(
            api_key=api_key,
            api_secret=api_secret,
            create_new=create_new,
            sync_approval=sync_approval,
        )

        secret_data = self.data.db.get_exchange_secrets()
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_port = settings['eth_rpc_port']
        self.trades_historian = TradesHistorian(
            user_directory=self.user_directory,
            db=self.data.db,
            eth_accounts=self.data.get_eth_accounts(),
            historical_data_start=historical_data_start,
        )
        self.inquirer = Inquirer(data_dir=self.data_dir, kraken=self.kraken)
        price_historian = PriceHistorian(
            data_directory=self.data_dir,
            history_date_start=historical_data_start,
            inquirer=self.inquirer,
        )
        db_settings = self.data.db.get_settings()
        self.accountant = Accountant(
            price_historian=price_historian,
            profit_currency=self.data.main_currency(),
            user_directory=self.user_directory,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets(),
            include_crypto2crypto=db_settings['include_crypto2crypto'],
            taxfree_after_period=db_settings['taxfree_after_period'],
            include_gas_costs=db_settings['include_gas_costs'],
        )

        # Initialize the rotkehlchen logger
        LoggingSettings(anonymized_logs=db_settings['anonymized_logs'])
        self.initialize_exchanges(secret_data)

        ethchain = Ethchain(eth_rpc_port)
        self.blockchain = Blockchain(
            blockchain_accounts=self.data.db.get_blockchain_accounts(),
            all_eth_tokens=self.data.eth_tokens,
            owned_eth_tokens=self.data.db.get_owned_tokens(),
            inquirer=self.inquirer,
            ethchain=ethchain,
        )
Exemplo n.º 3
0
    def unlock_user(self, user, password, create_new, sync_approval, api_key,
                    api_secret):
        # unlock or create the DB
        self.password = password
        user_dir = self.data.unlock(user, password, create_new)
        self.try_premium_at_start(api_key, api_secret, create_new,
                                  sync_approval, user_dir)

        secret_data = self.data.db.get_exchange_secrets()
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_port = settings['eth_rpc_port']
        self.trades_historian = TradesHistorian(
            self.data_dir,
            self.data.db,
            self.data.get_eth_accounts(),
            historical_data_start,
        )
        self.price_historian = PriceHistorian(
            self.data_dir,
            historical_data_start,
        )
        db_settings = self.data.db.get_settings()
        self.accountant = Accountant(
            price_historian=self.price_historian,
            profit_currency=self.data.main_currency(),
            user_directory=user_dir,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets(),
            include_crypto2crypto=db_settings['include_crypto2crypto'],
            taxfree_after_period=db_settings['taxfree_after_period'],
        )

        self.inquirer = Inquirer(kraken=self.kraken)
        self.initialize_exchanges(secret_data)

        ethchain = Ethchain(eth_rpc_port)
        self.blockchain = Blockchain(
            blockchain_accounts=self.data.db.get_blockchain_accounts(),
            all_eth_tokens=self.data.eth_tokens,
            owned_eth_tokens=self.data.db.get_owned_tokens(),
            inquirer=self.inquirer,
            ethchain=ethchain,
        )
Exemplo n.º 4
0
    def unlock_user(self, user, password, create_new, sync_approval, api_key,
                    api_secret):
        # unlock or create the DB
        self.password = password
        user_dir = self.data.unlock(user, password, create_new)
        self.try_premium_at_start(api_key, api_secret, create_new,
                                  sync_approval, user_dir)

        secret_data = self.data.db.get_exchange_secrets()
        self.cache_data_filename = os.path.join(self.data_dir,
                                                'cache_data.json')
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_port = settings['eth_rpc_port']
        self.trades_historian = TradesHistorian(
            self.data_dir,
            self.data.db,
            self.data.get_eth_accounts(),
            historical_data_start,
        )
        self.price_historian = PriceHistorian(
            self.data_dir,
            historical_data_start,
        )
        self.accountant = Accountant(
            price_historian=self.price_historian,
            profit_currency=self.data.main_currency(),
            user_directory=user_dir,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets())

        self.inquirer = Inquirer(kraken=self.kraken)
        self.initialize_exchanges(secret_data)

        ethchain = Ethchain(eth_rpc_port)
        self.blockchain = Blockchain(self.data.db.get_blockchain_accounts(),
                                     self.data.eth_tokens,
                                     self.data.db.get_owned_tokens(),
                                     self.inquirer, ethchain)
Exemplo n.º 5
0
def ethchain_client(ethrpc_port):
    return Ethchain(ethrpc_port, attempt_connect=False)
Exemplo n.º 6
0
def ethchain_client(ethrpc_port, etherscan):
    ethrpc_endpoint = f'http://localhost:{ethrpc_port}'
    return Ethchain(ethrpc_endpoint,
                    etherscan=etherscan,
                    attempt_connect=False)
Exemplo n.º 7
0
    def unlock_user(
        self,
        user: str,
        password: str,
        create_new: bool,
        sync_approval: bool,
        api_key: ApiKey,
        api_secret: ApiSecret,
    ) -> None:
        """Unlocks an existing user or creates a new one if `create_new` is True"""
        log.info(
            'Unlocking user',
            user=user,
            create_new=create_new,
            sync_approval=sync_approval,
        )
        # unlock or create the DB
        self.password = password
        self.user_directory = self.data.unlock(user, password, create_new)
        self.last_data_upload_ts = self.data.db.get_last_data_upload_ts()
        self.premium_sync_manager = PremiumSyncManager(data=self.data,
                                                       password=password)
        try:
            self.premium = self.premium_sync_manager.try_premium_at_start(
                api_key=api_key,
                api_secret=api_secret,
                username=user,
                create_new=create_new,
                sync_approval=sync_approval,
            )
        except AuthenticationError:
            # It means that our credentials were not accepted by the server
            # or some other error happened
            pass

        exchange_credentials = self.data.db.get_exchange_credentials()
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_endpoint = settings['eth_rpc_endpoint']
        self.trades_historian = TradesHistorian(
            user_directory=self.user_directory,
            db=self.data.db,
            eth_accounts=self.data.get_eth_accounts(),
            msg_aggregator=self.msg_aggregator,
        )
        # Initialize the price historian singleton
        PriceHistorian(
            data_directory=self.data_dir,
            history_date_start=historical_data_start,
            cryptocompare=Cryptocompare(data_directory=self.data_dir),
        )
        db_settings = self.data.db.get_settings()
        self.accountant = Accountant(
            profit_currency=self.data.main_currency(),
            user_directory=self.user_directory,
            msg_aggregator=self.msg_aggregator,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets(),
            include_crypto2crypto=db_settings['include_crypto2crypto'],
            taxfree_after_period=db_settings['taxfree_after_period'],
            include_gas_costs=db_settings['include_gas_costs'],
        )

        # Initialize the rotkehlchen logger
        LoggingSettings(anonymized_logs=db_settings['anonymized_logs'])
        self.initialize_exchanges(exchange_credentials)

        ethchain = Ethchain(eth_rpc_endpoint)
        self.blockchain = Blockchain(
            blockchain_accounts=self.data.db.get_blockchain_accounts(),
            owned_eth_tokens=self.data.db.get_owned_tokens(),
            ethchain=ethchain,
            msg_aggregator=self.msg_aggregator,
        )
        self.user_is_logged_in = True
Exemplo n.º 8
0
class Blockchain(object):

    def __init__(
            self,
            blockchain_accounts,
            all_eth_tokens,
            owned_eth_tokens,
            inquirer,
            ethrpc_port
    ):
        self.lock = Semaphore()
        self.results_cache = {}
        self.ethchain = Ethchain(ethrpc_port)
        self.inquirer = inquirer

        self.accounts = blockchain_accounts
        self.owned_eth_tokens = owned_eth_tokens

        # All the known tokens, along with addresses and decimals
        self.all_eth_tokens = {}
        for token in all_eth_tokens:
            try:
                token_symbol = str(token['symbol'])
            except (UnicodeDecodeError, UnicodeEncodeError):
                # skip tokens with problems in unicode encoding decoding
                continue

            self.all_eth_tokens[token_symbol] = {
                'address': token['address'],
                'decimal': token['decimal']
            }
        # Per account balances
        self.balances = defaultdict(dict)
        # Per asset total balances
        self.totals = defaultdict(dict)

    def set_eth_rpc_port(self, port):
        return self.ethchain.set_rpc_port(port)

    @property
    def eth_tokens(self):
        return self.owned_eth_tokens

    @cache_response_timewise()
    def query_balances(self):
        self.query_ethereum_balances()
        self.query_btc_balances()
        return {'per_account': self.balances, 'totals': self.totals}

    def query_btc_account_balance(self, account):
        btc_resp = urlopen(Request(
            'https://blockchain.info/q/addressbalance/%s' % account
        ))
        return FVal(btc_resp.read()) * FVal('0.00000001')  # result is in satoshis

    def query_btc_balances(self):
        if 'BTC' not in self.accounts:
            return

        self.balances['BTC'] = {}
        btc_usd_price = self.inquirer.find_usd_price('BTC')
        total = FVal(0)
        for account in self.accounts['BTC']:
            balance = self.query_btc_account_balance(account)
            total += balance
            self.balances['BTC'][account] = {'amount': balance, 'usd_value': balance * btc_usd_price}

        self.totals['BTC'] = {'amount': total, 'usd_value': total * btc_usd_price}

    def track_new_tokens(self, tokens):
        intersection = set(tokens).intersection(set(self.owned_eth_tokens))
        if intersection != set():
            raise InputError('Some of the new provided tokens to track already exist')

        self.owned_eth_tokens.extend(tokens)
        self.query_ethereum_tokens(tokens, self.balances['ETH'])
        return {'per_account': self.balances, 'totals': self.totals}

    def remove_eth_tokens(self, tokens):
        for token in tokens:
            usd_price = self.inquirer.find_usd_price(token)
            for account, account_data in self.balances['ETH'].items():
                if token not in account_data:
                    continue

                balance = account_data[token]
                deleting_usd_value = balance * usd_price
                del self.balances['ETH'][account][token]
                self.balances['ETH'][account]['usd_value'] = (
                    self.balances['ETH'][account]['usd_value'] -
                    deleting_usd_value
                )

            del self.totals[token]
            self.owned_eth_tokens.remove(token)

        return {'per_account': self.balances, 'totals': self.totals}

    def modify_btc_account(self, account, append_or_remove, add_or_sub):
        """Either appends or removes a BTC acccount.

        Call with 'append', operator.add to add the account
        Call with 'remove', operator.sub to remove the account
        """
        getattr(self.accounts['BTC'], append_or_remove)(account)
        btc_usd_price = self.inquirer.find_usd_price('BTC')
        balance = self.query_btc_account_balance(account)
        usd_balance = balance * btc_usd_price
        if append_or_remove == 'append':
            self.balances['BTC'][account] = {'amount': balance, 'usd_value': usd_balance}
        elif append_or_remove == 'remove':
            del self.balances['BTC'][account]
        else:
            raise ValueError('Programmer error: Should be append or remove')
        self.totals['BTC']['amount'] = add_or_sub(self.totals['BTC'].get('amount', 0), balance)
        self.totals['BTC']['usd_value'] = add_or_sub(
            self.totals['BTC'].get('usd_value', 0),
            usd_balance
        )

    def modify_eth_account(self, account, append_or_remove, add_or_sub):
        """Either appends or removes an ETH acccount.

        Call with 'append', operator.add to add the account
        Call with 'remove', operator.sub to remove the account
        """
        getattr(self.accounts['ETH'], append_or_remove)(account)
        eth_usd_price = self.inquirer.find_usd_price('ETH')
        balance = self.ethchain.get_eth_balance(account)
        usd_balance = balance * eth_usd_price
        if append_or_remove == 'append':
            self.balances['ETH'][account] = {'ETH': balance, 'usd_value': usd_balance}
        elif append_or_remove == 'remove':
            del self.balances['ETH'][account]
        else:
            raise ValueError('Programmer error: Should be append or remove')
        self.totals['ETH']['amount'] = add_or_sub(self.totals['ETH'].get('amount', 0), balance)
        self.totals['ETH']['usd_value'] = add_or_sub(
            self.totals['ETH'].get('usd_value', 0),
            usd_balance
        )

        for token in self.owned_eth_tokens:
            usd_price = self.inquirer.find_usd_price(token)
            if usd_price == 0:
                # skip tokens that have no price
                continue

            token_balance = self.ethchain.get_token_balance(
                token,
                self.all_eth_tokens[token]['address'],
                self.all_eth_tokens[token]['decimal'],
                account
            )
            if token_balance == 0:
                continue

            usd_value = token_balance * usd_price
            if append_or_remove == 'append':
                account_balance = self.balances['ETH'][account]
                account_balance[token] = balance
                account_balance['usd_value'] = account_balance['usd_value'] + usd_value

            self.totals[token] = {
                'amount': add_or_sub(self.totals[token].get('amount', 0), token_balance),
                'usd_value': add_or_sub(self.totals.get('usd_value', 0), usd_value)
            }

    def add_blockchain_account(self, blockchain, account):
        return self.modify_blockchain_account(blockchain, account, 'append', operator.add)

    def remove_blockchain_account(self, blockchain, account):
        return self.modify_blockchain_account(blockchain, account, 'remove', operator.sub)

    def modify_blockchain_account(self, blockchain, account, append_or_remove, add_or_sub):

        if blockchain == 'BTC':
            if append_or_remove == 'remove' and account not in self.accounts['BTC']:
                raise InputError('Tried to remove a non existing BTC account')
            self.modify_btc_account(account, append_or_remove, add_or_sub)

        elif blockchain == 'ETH':
            if append_or_remove == 'remove' and account not in self.accounts['ETH']:
                raise InputError('Tried to remove a non existing ETH account')
            self.modify_eth_account(account, append_or_remove, add_or_sub)
        else:
            raise InputError(
                'Unsupported blockchain {} provided at remove_blockchain_account'.format(
                    blockchain)
            )

        return {'per_account': self.balances, 'totals': self.totals}

    def query_ethereum_tokens(self, tokens, eth_balances):
        token_balances = {}
        token_usd_price = {}
        for token in tokens:
            usd_price = self.inquirer.find_usd_price(token)
            if usd_price == 0:
                # skip tokens that have no price
                continue
            token_usd_price[token] = usd_price

            token_balances[token] = self.ethchain.get_multitoken_balance(
                token,
                self.all_eth_tokens[token]['address'],
                self.all_eth_tokens[token]['decimal'],
                self.accounts['ETH'],
            )

        for token, token_accounts in token_balances.items():
            token_total = FVal(0)
            for account, balance in token_accounts.items():
                token_total += balance
                usd_value = balance * token_usd_price[token]
                eth_balances[account][token] = balance
                eth_balances[account]['usd_value'] = eth_balances[account]['usd_value'] + usd_value

            self.totals[token] = {
                'amount': token_total,
                'usd_value': token_total * token_usd_price[token]
            }

        self.balances['ETH'] = eth_balances

    def query_ethereum_balances(self):
        if 'ETH' not in self.accounts:
            return

        eth_accounts = self.accounts['ETH']
        eth_usd_price = self.inquirer.find_usd_price('ETH')
        balances = self.ethchain.get_multieth_balance(eth_accounts)
        eth_total = FVal(0)
        eth_balances = {}
        for account, balance in balances.items():
            eth_total += balance
            eth_balances[account] = {'ETH': balance, 'usd_value': balance * eth_usd_price}

        self.totals['ETH'] = {'amount': eth_total, 'usd_value': eth_total * eth_usd_price}
        self.balances['ETH'] = eth_balances  # but they are not complete until token query

        # And now for tokens
        self.query_ethereum_tokens(self.owned_eth_tokens, eth_balances)