Ejemplo n.º 1
0
    def data(self, index, role=None):
        if index.isValid():
            col_idx = index.column()
            row_idx = index.row()
            if row_idx < len(self.utxos):
                utxo = self.utxos[row_idx]
                if utxo:
                    if role in (Qt.DisplayRole, Qt.EditRole):
                        col = self.col_by_index(col_idx)
                        if col:
                            field_name = col.name
                            if field_name == 'satoshis':
                                return app_utils.to_string(
                                    round(utxo.satoshis / 1e8, 8))
                            elif field_name == 'masternode':
                                if utxo.masternode:
                                    return utxo.masternode.name
                            elif field_name == 'confirmations':
                                if utxo.block_height >= UNCONFIRMED_TX_BLOCK_HEIGHT:
                                    return 'Unconfirmed'
                                else:
                                    return app_utils.to_string(
                                        utxo.__getattribute__(field_name))
                            elif field_name == 'address':
                                if utxo.address_obj and utxo.address_obj.label:
                                    return utxo.address_obj.label
                                else:
                                    return utxo.address
                            elif col.name == 'txid':
                                if self.tx_explorer_url:
                                    url = self.tx_explorer_url.replace(
                                        '%TXID%', utxo.txid)
                                    url = f'<a href="{url}">{utxo.txid}</a>'
                                    return url
                                else:
                                    return utxo.txid
                            else:
                                return app_utils.to_string(
                                    utxo.__getattribute__(field_name))
                    elif role == Qt.ForegroundRole:
                        if utxo.is_collateral:
                            return QColor(Qt.white)
                        elif utxo.coinbase_locked or utxo.block_height >= UNCONFIRMED_TX_BLOCK_HEIGHT:
                            return QColor('red')

                    elif role == Qt.BackgroundRole:
                        if utxo.is_collateral:
                            return QColor(Qt.red)

                    elif role == Qt.TextAlignmentRole:
                        col = self.col_by_index(col_idx)
                        if col:
                            if col.name in ('satoshis', 'confirmations',
                                            'output_index'):
                                return Qt.AlignRight

        return QVariant()
 def display_second_unit_value(self):
     if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
         if self.value_percent is not None:
             self.lbl_second_unit_value.setText(
                 app_utils.to_string(round(self.value_percent, 3)) + '%')
         else:
             self.lbl_second_unit_value.setText('')
     elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
         if self.value_amount is not None:
             self.lbl_second_unit_value.setText(
                 app_utils.to_string(round(self.value_amount, 8)) + ' Dash')
         else:
             self.lbl_second_unit_value.setText('')
    def load_utxos_thread(self, ctrl):
        try:
            if not self.zyrkd_intf.open():
                self.errorMsg('Zyrk daemon not connected')
            else:
                try:
                    self.block_count = self.zyrkd_intf.getblockcount()
                    self.utxos = self.zyrkd_intf.getaddressutxos([self.dash_address])
                    self.utxos = [utxo for utxo in self.utxos if utxo['satoshis'] == 4000000000000 ]

                    try:
                        # for each utxo read block time
                        for utxo in self.utxos:
                            blockhash = self.zyrkd_intf.getblockhash(utxo.get('height'))
                            bh = self.zyrkd_intf.getblockheader(blockhash)
                            utxo['time_str'] = app_utils.to_string(datetime.datetime.fromtimestamp(bh['time']))
                            utxo['confirmations'] = self.block_count - bh.get('height') + 1
                    except Exception as e:
                        self.errorMsg(str(e))

                except DashdIndexException as e:
                    self.errorMsg(str(e))

                except Exception as e:
                    self.errorMsg('Error occurred while calling getaddressutxos method: ' + str(e))
        except Exception as e:
            pass
    def set_input_amount(self, amount, inputs_count):
        self.inputs_count = inputs_count
        if amount != self.inputs_total_amount or inputs_count != self.inputs_count:
            # if there is only one recipient address and his current amount equals to the
            # previuus input_amount, assign new value to him

            last_total_amount = self.inputs_total_amount
            last_fee_amount = self.fee_amount
            self.inputs_total_amount = amount
            self.fee_amount = self.calculate_fee()

            if (len(self.recipients) == 1
                    or self.recipients[0].get_value(default_value=0.0) == 0.0
                    or self.recipients[0].get_value(default_value=0.0)
                    == round(last_total_amount - last_fee_amount, 8)):

                if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
                    amount_minus_fee = round(amount - self.fee_amount, 8)
                    if amount_minus_fee < 0:
                        amount_minus_fee = 0.0
                    self.recipients[0].set_value(amount_minus_fee)
                elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                    self.recipients[0].set_value(100.0)

            old_state = self.edt_fee_value.blockSignals(True)
            self.edt_fee_value.setText(app_utils.to_string(self.fee_amount))
            self.edt_fee_value.blockSignals(old_state)

            for addr in self.recipients:
                addr.set_inputs_total_amount(amount - self.fee_amount)
                addr.clear_validation_results()

            self.edt_fee_value.update()
            self.update_change_amount()
            self.display_totals()
 def set_output_value_unit(self, unit):
     old_state = self.edt_amount.blockSignals(True)
     try:
         if unit == OUTPUT_VALUE_UNIT_AMOUNT:
             self.edt_amount.setText(app_utils.to_string(self.value_amount))
             self.lbl_amount.setText('value')
         elif unit == OUTPUT_VALUE_UNIT_PERCENT:
             self.edt_amount.setText(app_utils.to_string(
                 self.value_percent))
             self.lbl_amount.setText('pct. value')
         else:
             raise Exception('Invalid unit')
         self.values_unit = unit
         self.display_second_unit_value()
     finally:
         self.edt_amount.blockSignals(old_state)
     self.edt_amount.update()
    def set_default_fee(self):
        self.fee_amount = self.calculate_fee()

        old_status = self.edt_fee_value.blockSignals(True)
        try:
            self.edt_fee_value.setText(app_utils.to_string(self.fee_amount))
        finally:
            self.edt_fee_value.blockSignals(old_status)
        self.edt_fee_value.update()
        self.init_calculate_change_value()
    def set_value(self, value):
        old_state = self.edt_amount.blockSignals(True)
        try:
            if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT:
                self.value_amount = value
            elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT:
                self.value_percent = value
            self.re_calculate_second_unit_value()

            self.edt_amount.setText(app_utils.to_string(value))
        finally:
            self.edt_amount.blockSignals(old_state)
        self.edt_amount.update()
    def data(self, index, role=None):
        if index.isValid():
            col = index.column()
            row = index.row()
            if row < len(self.utxos):
                utxo = self.utxos[row]
                if utxo:
                    if role in (Qt.DisplayRole, Qt.EditRole):
                        field_name = self.columns[col][0]
                        if field_name == 'satoshis':
                            return app_utils.to_string(round(utxo['satoshis'] / 1e8, 8))
                        else:
                            return app_utils.to_string(utxo.get(field_name, ''))

                    elif role == Qt.ForegroundRole:
                        if utxo['collateral']:
                            return QColor(Qt.red)
                        elif utxo['coinbase_locked']:
                            if col == 1:
                                return QtGui.QColor('red')
                            else:
                                return QtGui.QColor('gray')
                        elif utxo.get('spent_date'):
                            return QtGui.QColor('black')  # spen transactions

                    elif role == Qt.BackgroundRole:
                        if utxo['coinbase_locked']:
                            return QtGui.QColor('lightgray')
                        elif utxo.get('spent_date'):
                            return QtGui.QColor('lightgray')  # spent transactions

                    elif role == Qt.TextAlignmentRole:
                        if col in (0, 1):
                            return Qt.AlignRight

        return QVariant()
    def load_utxos_thread(self, ctrl: CtrlObject, new_utxos_out, existing_utxos_out):
        """
        Thread gets UTXOs from the network and returns the new items (not existing in the self.utxo list)
        :param ctrl:
        :param new_utxos_out: Here will be returned all the new UTXOs.
        :param existing_utxos_out: In this list will be returned all UTXOs which existed in the self.utxo list before
        """
        ADDRESS_CHUNK = 10
        if not self.finishing and not ctrl.finish:
            self.set_message(f'Reading unspent transaction outputs...')

        def get_addresses_to_scan(self, thread_ctrl: CtrlObject, addr_scan_ctrl: dict):
            """
            :param self:
            :param addr_scan_ctrl: (only for self.utxo_src_mode == 2) penultimate element of bip32 path to scan, used
                to switch sanning between normal and change addresses
            :return: yield List[Tuple[str (address), str (bip32 path)]]
            """
            try:
                if self.utxo_src_mode == 1:

                    if self.mn_src_index is not None:
                        if self.mn_src_index == len(self.masternode_addresses):
                            # show addresses of all masternodes
                            for chunk_nr in range(int(math.ceil(len(self.masternode_addresses) / ADDRESS_CHUNK))):
                                if self.finishing or thread_ctrl.finish:
                                    return
                                yield [x for x in self.masternode_addresses[
                                                  chunk_nr * ADDRESS_CHUNK : (chunk_nr + 1) * ADDRESS_CHUNK] if x[0] and x[1]]
                        elif self.mn_src_index < len(self.masternode_addresses) and self.mn_src_index >= 0:
                            if self.finishing or thread_ctrl.finish:
                                return
                            if self.masternode_addresses[self.mn_src_index][0] and \
                                self.masternode_addresses[self.mn_src_index][1]:
                                yield [self.masternode_addresses[self.mn_src_index]]

                elif self.utxo_src_mode == 2:
                    # hw wallet account: scan all addresses and change addresses for a specific account
                    # stop when a defined number of subsequent address has balance 0

                    addr_count = 0
                    addr_n = gobyte_utils.bip32_path_string_to_n(self.hw_account_base_bip32_path)
                    db_cur = self.db_intf.get_cursor()

                    try:
                        bip32_path_n = addr_n[:] + [self.hw_account_number + 0x80000000, 0, 0]
                        cur_addr_buf = []
                        last_level2_nr = addr_scan_ctrl.get('level2')
                        while True:
                            restart_iteration = False
                            for nr in range(1000):
                                if self.finishing or thread_ctrl.finish:
                                    return
                                if last_level2_nr != addr_scan_ctrl.get('level2'):
                                    last_level2_nr = addr_scan_ctrl.get('level2')
                                    restart_iteration = True
                                    break
                                bip32_path_n[-2] = addr_scan_ctrl.get('level2')
                                bip32_path_n[-1] = nr

                                cur_addr = hw_intf.get_address_ext(self.main_ui.hw_session, bip32_path_n, db_cur,
                                                                   self.app_config.hw_encrypt_string,
                                                                   self.app_config.hw_decrypt_string)

                                bip32_path = gobyte_utils.bip32_path_n_to_string(bip32_path_n)
                                cur_addr_buf.append((cur_addr, bip32_path))
                                addr_count += 1
                                if len(cur_addr_buf) >= ADDRESS_CHUNK:
                                    yield cur_addr_buf
                                    cur_addr_buf.clear()
                            if restart_iteration:
                                continue
                            if cur_addr_buf:
                                yield cur_addr_buf
                            break
                    finally:
                        if db_cur.connection.total_changes > 0:
                            self.db_intf.commit()
                        self.db_intf.release_cursor()

                elif self.utxo_src_mode == 3:

                    db_cur = self.db_intf.get_cursor()
                    try:
                        # address from a specific bip32 path
                        bip32_path_n = gobyte_utils.bip32_path_string_to_n(self.hw_src_bip32_path)
                        cur_addr = hw_intf.get_address_ext(self.main_ui.hw_session, bip32_path_n, db_cur,
                                                           self.app_config.hw_encrypt_string,
                                                           self.app_config.hw_decrypt_string)
                        self.hw_src_address = cur_addr
                        yield [(cur_addr, self.hw_src_bip32_path)]

                    finally:
                        if db_cur.connection.total_changes > 0:
                            self.db_intf.commit()
                        self.db_intf.release_cursor()

            except Exception as e:
                logging.exception('Exception occurred')
                raise

        if not self.gobyted_intf.open():
            self.errorMsg('GoByte daemon not connected')
        else:
            tm_begin = time.time()
            try:
                cur_block_height = self.gobyted_intf.getblockcount()
                self.sel_addresses_balance = 0
                self.sel_addresses_received = 0
                addr_count = 0
                utxos_count = 0
                addr_scan_ctrl = {'level2': 0}  # the one before last elemnt of bip32 paths to scan (0: normal address,
                                                # 1: change address

                for addr_path_chunk in get_addresses_to_scan(self, ctrl, addr_scan_ctrl):
                    try:
                        if self.finishing or ctrl.finish:
                            break

                        addr_chunk = []
                        addr_to_bip32 = {}
                        logging.debug(f'Got BIP32path-address pair chunk: {len(addr_path_chunk)}')
                        for a, p in addr_path_chunk:
                            addr_chunk.append(a)
                            addr_to_bip32[a] = p
                            logging.debug(f'Adding BIP32path-address pair for UTXO load: {p} - {a}')

                        balance = self.gobyted_intf.getaddressbalance(addr_chunk)
                        if balance.get('received') == 0:
                            if addr_scan_ctrl['level2'] == 0:
                                addr_scan_ctrl['level2'] = 1  # switch to change addresses
                                continue
                            else:
                                break
                        else:
                            self.sel_addresses_received += balance.get('received')

                        if balance.get('balance') > 0:
                            self.sel_addresses_balance += balance.get('balance')
                            # get utxos for addresses
                            uxs = self.gobyted_intf.getaddressutxos(addr_chunk)
                            utxos_count += len(uxs)

                            addr_count += len(addr_path_chunk)
                            for idx, utxo in enumerate(uxs):
                                if self.finishing or ctrl.finish:
                                    return

                                utxo_key = utxo['txid'] + '-' + str(utxo['outputIndex'])
                                cached_utxo = self.utxos_dict.get(utxo_key)
                                if not cached_utxo:
                                    blockhash = self.gobyted_intf.getblockhash(utxo.get('height'))
                                    bh = self.gobyted_intf.getblockheader(blockhash)
                                    utxo['key'] = utxo_key
                                    utxo['time_str'] = app_utils.to_string(datetime.datetime.fromtimestamp(bh['time']))
                                    utxo['confirmations'] = cur_block_height - utxo.get('height') + 1
                                    utxo['coinbase_locked'] = False
                                    utxo['bip32_path'] = addr_to_bip32.get(utxo['address'])
                                    if not utxo['bip32_path']:
                                        logging.warning(f'BIP32 path not found for address: {utxo["address"]}')

                                    try:
                                        # verify whether it's a coinbase transaction and if so,if it has
                                        # enough confirmations to spend
                                        rawtx = self.gobyted_intf.getrawtransaction(utxo.get('txid'), 1)
                                        if rawtx:
                                            self.rawtransactions[utxo.get('txid')] = rawtx['hex']
                                            vin = rawtx.get('vin')
                                            if len(vin) == 1 and vin[0].get('coinbase') and utxo['confirmations'] < 100:
                                                utxo['coinbase_locked'] = True
                                    except Exception:
                                        logging.exception('Error while verifying transaction coinbase')

                                    new_utxos_out.append(utxo)
                                else:
                                    cached_utxo['confirmations'] = cur_block_height - utxo.get('height') + 1
                                    if cached_utxo['coinbase_locked']:
                                        cached_utxo['coinbase_locked'] = (cached_utxo['confirmations'] < 100)
                                    existing_utxos_out.append(cached_utxo)

                            if not self.finishing and not ctrl.finish:
                                self.set_message(f'Reading unspent transaction outputs... '
                                                 f'Address count: {addr_count}, UTXOs count: {utxos_count}')
                    except Exception as e:
                        logging.exception('Exception occurred')
                        raise

                self.sel_addresses_balance = round(self.sel_addresses_balance / 1e8, 8)
                self.sel_addresses_received = round(self.sel_addresses_received / 1e8, 8)

                tm_diff = time.time() - tm_begin
                logging.info(f'load_utxos_thread exec time: {tm_diff} s')
            except GobytedIndexException as e:
                self.errorMsg(str(e))

            except Exception as e:
                self.errorMsg('Error occurred while calling getaddressutxos method: ' + str(e))
    def data(self, index, role=None):
        if index.isValid():
            col_idx = index.column()
            row_idx = index.row()
            col = self.col_by_index(col_idx)
            if row_idx < len(self.txes):
                tx = self.txes[row_idx]
                if role in (Qt.DisplayRole, Qt.EditRole):
                    if col.name == 'direction':
                        if tx.direction == 1:
                            if tx.is_coinbase:
                                return 'In - New coins'
                            else:
                                return 'In'
                        else:
                            return 'Out'
                    elif col.name == 'satoshis':
                        return app_utils.to_string(round(tx.satoshis / 1e8, 8))
                    elif col.name == 'senders':
                        return tx
                    elif col.name == 'recipient':
                        return tx
                    elif col.name == 'block_height':
                        if tx.block_height == UNCONFIRMED_TX_BLOCK_HEIGHT:
                            return 0
                        else:
                            return tx.block_height
                    elif col.name == 'tx_hash':
                        if self.tx_explorer_url:
                            url = self.tx_explorer_url.replace('%TXID%', tx.tx_hash)
                            url = f'<a href="{url}">{tx.tx_hash}</a>'
                            return url
                        else:
                            return tx.tx_hash
                    elif col.name == 'confirmations':
                        if self.__current_block_height is None:
                            return ''
                        else:
                            if tx.block_height == UNCONFIRMED_TX_BLOCK_HEIGHT:
                                return 'Unconfirmed'
                            else:
                                return app_utils.to_string(self.__current_block_height - tx.block_height + 1)
                    else:
                        return app_utils.to_string(tx.__getattribute__(col.name))
                elif role == Qt.ForegroundRole:
                    if col.name == 'direction':
                        if tx.direction == 1:
                            if tx.is_coinbase:
                                return QtGui.QColor(Qt.darkBlue)
                            else:
                                return QtGui.QColor(Qt.darkGreen)
                        else:
                            return QtGui.QColor(Qt.red)
                    elif col.name == 'satoshis':
                        if tx.satoshis < 0:
                            return QtGui.QColor(Qt.red)

                elif role == Qt.BackgroundRole:
                    pass

                elif role == Qt.TextAlignmentRole:
                    col = self.col_by_index(col_idx)
                    if col:
                        if col.name in ('satoshis', 'block_height', 'confirmations'):
                            return Qt.AlignRight
                        else:
                            return Qt.AlignLeft
        return QVariant()
Ejemplo n.º 11
0
    def data(self, index, role=None):
        if index.isValid():
            col_idx = index.column()
            row_idx = index.row()
            if row_idx < len(self.utxos):
                utxo = self.utxos[row_idx]
                if utxo:
                    if role in (Qt.DisplayRole, Qt.EditRole):
                        col = self.col_by_index(col_idx)
                        if col:
                            field_name = col.name
                            if field_name == 'address':
                                if utxo.address:
                                    return utxo.address
                                else:
                                    return '???'
                            elif field_name == 'bip32_path':
                                if utxo.address_obj:
                                    return utxo.address_obj.bip32_path
                                else:
                                    return '???'
                            elif field_name == 'assigned_to_mn':
                                mn = self.get_utxo_mn_assignement(utxo)
                                return mn.name if mn else ''
                            elif field_name == 'txid':
                                if self.tx_explorer_url:
                                    url = self.tx_explorer_url.replace(
                                        '%TXID%', utxo.txid)
                                    url = f'<a href="{url}">{utxo.txid}</a>'
                                    return url
                                else:
                                    return utxo.txid
                            elif field_name == 'output_index':
                                return utxo.output_index
                            elif field_name == 'time_stamp':
                                return utxo.time_str
                            elif field_name == 'confirmations':
                                if utxo.block_height == UNCONFIRMED_TX_BLOCK_HEIGHT:
                                    return 'Unconfirmed'
                                else:
                                    return app_utils.to_string(
                                        utxo.__getattribute__(field_name))
                            else:
                                return app_utils.to_string(
                                    utxo.__getattribute__(field_name))
                    elif role == Qt.ForegroundRole:
                        if utxo.is_collateral:
                            return QColor(Qt.red)
                        elif utxo.coinbase_locked:
                            if col_idx == 1:
                                return QColor('red')
                            else:
                                return QColor('gray')

                    elif role == Qt.BackgroundRole:
                        if utxo.coinbase_locked:
                            return QColor('lightgray')

                    elif role == Qt.TextAlignmentRole:
                        col = self.col_by_index(col_idx)
                        if col:
                            if col.name in ('satoshis', 'confirmations',
                                            'output_index'):
                                return Qt.AlignRight

        return QVariant()