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