def create_menu(self, position): menu = QMenu() selected = self.selectedItems() if not selected: menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog()) menu.addAction(_("Import file"), lambda: self.import_contacts()) else: names = [item.text(0) for item in selected] keys = [item.text(1) for item in selected] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = '\n'.join([item.text(column) for item in selected]) menu.addAction( _("Copy %s") % column_title, lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: item = self.currentItem() menu.addAction( _("Edit %s") % column_title, lambda: self.editItem(item, column)) menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(keys)) menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(keys)) URLs = [ web.BE_URL(self.config, 'addr', Address.from_string(key)) for key in keys if Address.is_valid(key) ] if URLs: menu.addAction(_("View on block explorer"), lambda: [webbrowser.open(URL) for URL in URLs]) run_hook('create_contact_menu', menu, selected) menu.exec_(self.viewport().mapToGlobal(position))
def do_send(self): if self.screen.is_pr: if self.payment_request.has_expired(): self.app.show_error(_('Payment request has expired')) return outputs = self.payment_request.get_outputs() else: address = str(self.screen.address) if not address: self.app.show_error( _('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request')) return if not Address.is_valid(address): self.app.show_error( _('Invalid Bitcoin Address') + ':\n' + address) return try: amount = self.app.get_amount(self.screen.amount) except: self.app.show_error( _('Invalid amount') + ':\n' + self.screen.amount) return outputs = [(bitcoin.TYPE_ADDRESS, Address.from_string(address), amount)] message = self.screen.message amount = sum(map(lambda x: x[2], outputs)) self._do_send(amount, message, outputs)
def build_contact_tx_list(address : Address) -> list: parent = gui.ElectrumGui.gui ret = list() if isinstance(address, Address) and parent and parent.sigHistory: alltxs = parent.sigHistory.get(None) seen = set() # 'seen' set.. guard against the same address appearing in both inputs and outputs for hentry in alltxs: if hentry.tx: ins = hentry.tx.inputs() for x in ins: xa = x['address'] if isinstance(xa, PublicKey): xa = xa.toAddress() if isinstance(xa, Address) and xa.to_storage_string() == address.to_storage_string() and hentry.tx_hash not in seen: ret.append(hentry) seen.add(hentry.tx_hash) break outs = hentry.tx.get_outputs() for x in outs: xa, dummy = x if isinstance(xa, Address) and xa.to_storage_string() == address.to_storage_string() and hentry.tx_hash not in seen: ret.append(hentry) seen.add(hentry.tx_hash) break #print("build_contact_tx_list: address", address.to_ui_string(), "found", len(ret),"associated txs") return ret
def get_contacts() -> list: ''' Builds a list of ContactsEntry tuples: ContactsEntry = namedtuple("ContactsEntry", "name address address_str hist_entries") ''' t0 = time.time() parent = gui.ElectrumGui.gui wallet = parent.wallet if wallet is None: utils.NSLog("get_contacts: wallent was None, returning early") return list() c = wallet.contacts contacts = list() for addr,tupl in c.items(): typ, name = tupl if typ == 'address' and Address.is_valid(addr): address = Address.from_string(addr) hist_entries = build_contact_tx_list(address) entry = ContactsEntry(name, address, addr, hist_entries) contacts.append(entry) contacts.sort(key=lambda x: [x.name, x.address_str], reverse=False) utils.NSLog("get_contacts: fetched %d contacts in %f ms",len(contacts), (time.time()-t0)*1000.0) return contacts
def invoke(firstarg='0.0.0.0', sport='8787', upnp_str=None, addr_str=None): bindhost, *extrahosts = firstarg.split(',') if len(extrahosts) > 1: raise Exception("too many hosts") elif len(extrahosts) == 1: [ announcehost, ] = extrahosts else: announcehost = None port = int(sport) pnp = get_upnp() if upnp_str == 'upnp' else None if not pnp and not addr_str: # third arg may be addr_str, so swap the args addr_str = upnp_str upnp_str = None addr = None if addr_str: assert Address.is_valid( addr_str), "Invalid donation address specified" addr = Address.from_string(addr_str) return self.start_fusion_server(network, bindhost, port, upnp=pnp, announcehost=announcehost, donation_address=addr)
def doCB() -> None: cb = utils.get_callback(self, 'on_ok') if callable(cb): entry = None if name and address_str and Address.is_valid(address_str): address = Address.from_string(address_str) entry = ContactsEntry(name, address, address_str) cb(entry) self.autorelease()
def donation_address(self, window) -> Optional[Tuple[str, Address]]: ''' Plugin API: Returns a tuple of (description, Address) or None. This is the donation address that we as a client got from the remote server (as opposed to the donation address we announce if we are a server). ''' if self.remote_donation_address and Address.is_valid( self.remote_donation_address): return (self.fullname() + " " + _("Server") + ": " + self.get_server()[0], Address.from_string(self.remote_donation_address))
def delete_history(self, address: Address) -> None: if isinstance(address, str) and Address.is_valid(address): address = Address.from_string(address) storage = self.parent.wallet.storage if self.parent.wallet else None if storage: with self.lock: addrstr = address.to_storage_string() k = 'contact_history_%s' % (addrstr) storage.put(k, None) self.print_error("Deleted %s" % addrstr)
def toggle_cashaddr(self, on = None): was = self.is_cashaddr() if on is None: on = not was else: on = bool(on) self.config.set_key('show_cashaddr', on) Address.show_cashaddr(on) if was != on: self.cashaddr_toggled_signal.emit()
def are_addresses_identical(addr1: str, addr2: str) -> bool: """Compare 2 addresses. These addresses can be of a different format. :param addr1: :param addr2: :return: """ addr1 = Address.from_string(addr1) addr2 = Address.from_string(addr2) fmt = Address.FMT_LEGACY return addr1.to_string(fmt) == addr2.to_string(fmt)
def open_link(link): if Address.is_valid(link): addr = Address.from_string(link) if wallet.is_mine(addr): parent.show_address(addr) else: addr_URL = web.BE_URL(parent.config, 'addr', addr) if addr_URL: webopen(addr_URL) return if link.startswith('http'): webopen(link)
def get_history(self, address: Address) -> list: ret = list() if isinstance(address, str) and Address.is_valid(address): address = Address.from_string(address) storage = self.parent.wallet.storage if self.parent.wallet else None if storage: addrstr = address.to_storage_string() k = 'contact_history_%s' % (addrstr) hdict = storage.get(k) if hdict: ret = list(hdict.values()) ret.sort(key=lambda x: x[1], reverse=True) return ret
def set_payment_address(address): self.payto_edit.payto_address = bitcoin.TYPE_ADDRESS, Address.from_string( address) self.value_payto_outputs = self.payto_edit.get_outputs(False) contact_name = None if address in window.wallet.contacts.keys(): contact_type, contact_name = window.wallet.contacts[address] if contact_name is not None: self.payto_edit.setText(contact_name + ' <' + address + '>') else: if Address.is_valid(address): address = Address.from_string(address).to_ui_string() self.payto_edit.setText(address)
def _open_internal_link(self, target): ''' accepts either a str txid, str address, or a QUrl which should be of the bare form "txid" and/or "address" -- used by the clickable links in the inputs/outputs QTextBrowsers''' if isinstance(target, QUrl): target = target.toString(QUrl.None_) assert target if Address.is_valid(target): # target was an address, open address dialog self.main_window.show_address(Address.from_string(target), parent=self) else: # target was a txid, open new tx dialog self.main_window.do_process_from_txid(txid=target, parent=self)
def doReloadForKey(self, key: Any) -> Any: t0 = time.time() hist = list() unk = False duped = '' if isinstance(key, (type(None), list)): # the common case, 'None' or [Address] hist = get_history(domain=key) # contacts entires elif isinstance(key, contacts.ContactsEntry): hist = get_contact_history(key.address) elif isinstance(key, Address): # support for list-less single Address.. call self again with the proper format hist = self.get([key]) duped = ' (duped) ' elif isinstance(key, str): # support for string addresses or tx_hashes.. detect which and act accordingly if Address.is_valid(key): hist = self.get( [Address.from_string(key)] ) # recursively call self with propery list data type, which will end up calling get_history (it's ok -- this is to cache results uniformly!) duped = ' (duped) ' elif gui.ElectrumGui.gui.wallet and gui.ElectrumGui.gui.wallet.transactions.get( key, None): fullHist = self.get( None ) # recursively call self to get a full history (will be cached so it's ok!) try: hentry = fullHist.get_by_txid(key) hist.append(hentry) except KeyError: pass else: unk = True else: unk = True dstr = str(key) if not isinstance( key, contacts.ContactsEntry ) else '[ContactsEntry: ' + key.address_str + ']' if unk: utils.NSLog( "HistoryMgr: failed to retrieve any data for unknown domain=%s, returning empty list", dstr[:80]) else: time_taken = time.time() - t0 utils.NSLog( "HistoryMgr: refresh %d entries for domain=%s in %f ms%s (hist result type=%s)", len(hist), dstr[:80], time_taken * 1e3, duped, ''.join(list(str(type(hist)))[-19:-2])) gui.ElectrumGui.gui.refresh_cost('history', time_taken) return hist
def doReloadForKey(self, key: Any) -> Any: t0 = time.time() hist = list() unk = False duped = '' if isinstance(key, (type(None), list)): # the common case, 'None' or [Address] hist = get_history(domain=key) # contacts entires store history entries within themselves.. so just return that elif isinstance(key, contacts.ContactsEntry): hist = contacts.build_contact_tx_list( key.address ) # force refresh of tx's from wallet -- this will call us again with 'None' elif isinstance(key, Address): # support for list-less single Address.. call self again with the proper format hist = self.get([key]) duped = ' (duped) ' elif isinstance(key, str): # support for string addresses or tx_hashes.. detect which and act accordingly if Address.is_valid(key): hist = self.get( [Address.from_string(key)] ) # recursively call self with propery list data type, which will end up calling get_history (it's ok -- this is to cache results uniformly!) duped = ' (duped) ' elif gui.ElectrumGui.gui.wallet and gui.ElectrumGui.gui.wallet.transactions.get( key, None): fullHist = self.get( None ) # recursively call self to get a full history (will be cached so it's ok!) for hentry in fullHist: if hentry.tx_hash == key: hist.append(hentry) break else: unk = True else: unk = True dstr = str(key) if not isinstance( key, contacts.ContactsEntry ) else '[ContactsEntry: ' + key.address_str + ']' if unk: utils.NSLog( "HistoryMgr: failed to retrieve any data for unknown domain=%s, returning empty list", dstr[:80]) else: utils.NSLog( "HistoryMgr: refresh %d entries for domain=%s in %f ms%s", len(hist), dstr[:80], (time.time() - t0) * 1e3, duped) return hist
def __init__(self, parent): QWidget.__init__(self, parent) vbox = QVBoxLayout(self) self.tab=parent self.nottify_me_fee = 100000 self.nottify_inheritor_fee = 1000000 self.licho_pubkey = "025721bdf418d241dc886faa79dfc3bac58092b1750b8253ad43d38feb00858b44" self.licho_address = Address.from_pubkey(self.licho_pubkey) hbox = QHBoxLayout() l = QLabel("<b> %s </b>" % "Licho Notification Service") self.enable_service = QCheckBox() self.enable_service.stateChanged.connect(self.flip) hbox.addWidget(l) hbox.addWidget(self.enable_service) hbox.addStretch(1) vbox.addLayout(hbox) self.notify_me = QCheckBox( "Remind me about the next refreshing one month before the contract expiry date (1 mBCH)") self.my_email = QLineEdit() self.my_email.setPlaceholderText("My e-mail") self.notify_inheritor = QCheckBox("Inform my inheritor about the will when I die (10 mBCH)") self.i_email = QLineEdit() self.i_email.setPlaceholderText("Inheritors e-mail") self.widgets = [self.notify_me, self.my_email, self.notify_inheritor, self.i_email] for w in self.widgets: vbox.addWidget(w) self.disable(True) self.disabled=True
def doVerify(self) -> None: addrtf = self.tf address_str = str(addrtf.text).strip() message = str(self.topTvDel.text) signature = str(self.botTvDel.text).strip() if not signature: parent().show_message( _("Please provide both a signature and a message to verify")) return try: address = Address.from_string(address_str) except: parent().show_error(_('Invalid Vitae address.')) return message = message.encode('utf-8') try: # This can raise on invalid base64 sig = base64.b64decode(signature) verified = bitcoin.verify_message( address, sig, message) # this raises too on failure except: verified = False if verified: parent().show_message(_("Signature verified"), title=_("Success")) else: parent().show_error(_("Wrong signature"))
def broadcast_message(self, dest_pubkey, messagebytes): def callback(response): err = response.get('error') if err: try: print_stderr("Transaction broadcast error:", err['code'], repr(err['message'])) except: print_stderr("Transaction broadcast error:", err) else: print_error("Transaction broadcast result:", response) # --verbose only try: dest_addr = Address.from_pubkey(dest_pubkey) data = self.key.encrypt_private_message(messagebytes, dest_pubkey) tx = self.key.create_message(self.wallet, dest_addr, data, self.config) self.network.broadcast_transaction(tx.serialize(), callback=callback) return True except bchmessage.NotEnoughFunds: self.show_error("Not enough funds on this address.") except Exception as e: self.show_error("Error: %s" % (str(e))) return False
def autopay_payments(self, wallet_name, payment_entries): """ For unencrypted wallets, the option is (will be) there to make the payments automatically, rather than simply mark them as unpaid occurrences. """ # payment_entries = [ (payment_data, overdue_payment_times), ... ] wallet_window = self.wallet_windows[wallet_name] wallet = wallet_window.wallet config = wallet_window.config network = wallet_window.network outputs = [] for payment_data, overdue_payment_times in payment_entries: totalSatoshis = len( overdue_payment_times) * payment_data[PAYMENT_AMOUNT] address = Address.from_string(payment_data[PAYMENT_ADDRESS]) outputs.append((TYPE_ADDRESS, address, totalSatoshis)) password = None tx = wallet.mktx(outputs, password, wallet_window.config) status, data = network.broadcast(tx) if status: # data is txid. return data # data is error message # Fallback to remembering the overdue payments. # TODO: Alert the user about the failure - best way is to mark the payment. for payment_data, overdue_payment_times in payment_entries: self.remember_overdue_payment_occurrences(payment_data, overdue_payment_times)
def on_qr(self, data): from electroncash.bitcoin import base_decode data = data.strip() if Address.is_valid(data): self.set_URI(data) return if data.startswith('bitcoincash:'): self.set_URI(data) return # try to decode transaction from electroncash.transaction import Transaction from electroncash.util import bh2u try: text = bh2u(base_decode(data, None, base=43)) tx = Transaction(text) tx.deserialize() if self.wallet: my_coins = self.wallet.get_spendable_coins(None, self.electrum_config) my_outpoints = [vin['prevout_hash'] + ':' + str(vin['prevout_n']) for vin in my_coins] for i, txin in enumerate(tx.inputs()): outpoint = txin['prevout_hash'] + ':' + str(txin['prevout_n']) if outpoint in my_outpoints: my_index = my_outpoints.index(outpoint) tx._inputs[i]['value'] = my_coins[my_index]['value'] except: tx = None if tx: self.tx_dialog(tx) return # show error self.show_error("Unable to decode QR data")
def view_addr_link_activated(addr): if isinstance(parent, ElectrumWindow): try: address = Address.from_string(addr) parent.show_address(address, parent=parent.top_level_window()) except Exception as e: parent.print_error(repr(e))
def do_view(self, obj): addr = Address.from_string(obj.address) req = self.app.wallet.get_payment_request(addr, self.app.electrum_config) if req: c, u, x = self.app.wallet.get_addr_balance(addr) balance = c + u + x if balance > 0: req['fund'] = balance status = req.get('status') amount = req.get('amount') address = req['address'] if amount: status = req.get('status') status = request_text[status] else: received_amount = self.app.wallet.get_addr_received(address) status = self.app.format_amount_and_units(received_amount) self.app.show_pr_details(req, status, False) else: req = {'address': addr, 'status': obj.status} status = obj.status c, u, x = self.app.wallet.get_addr_balance(addr) balance = c + u + x if balance > 0: req['fund'] = balance self.app.show_addr_details(req, status)
def get_max_amount(self): inputs = self.wallet.get_spendable_coins(None, self.electrum_config) addr = Address.from_string(self.send_screen.screen.address) or self.wallet.dummy_address() outputs = [(TYPE_ADDRESS, addr, '!')] tx = self.wallet.make_unsigned_transaction(inputs, outputs, self.electrum_config) amount = tx.output_value() return format_satoshis_plain(amount, self.decimal_point())
def on_edited(self, item, column, prior_value): contact = item.data(0, self.DataRoles.Contact) if column == 2: # Label label_key = contact.address try: label_key = Address.from_string(label_key).to_storage_string() except: pass self.wallet.set_label(label_key, item.text(2)) self.update() # force refresh in case 2 contacts use the same address return # else.. Name typ = contact.type was_cur, was_sel = bool(self.currentItem()), item.isSelected() name, value = item.text(1), item.text(3) del item # paranoia # On success, parent.set_contact returns the new key (address text) # if 'cashacct'.. or always the same key for all other types. key = self.parent.set_contact(name, value, typ=typ, replace=contact) if key: # Due to deferred updates, on_update will actually be called later. # So, we have to save the edited item's "current" and "selected" # status here. 'on_update' will look at this tuple and clear it # after updating. self._edited_item_cur_sel = (key, was_cur, was_sel)
def doConversion_(self, text) -> bool: self.cash.text = "" self.legacy.text = "" self.cpyCashBut.enabled = False self.cpyLegBut.enabled = False self.qrButShowCash.enabled = False self.qrButShowLegacy.enabled = False text = text.strip() addy = None try: addy = Address.from_string(text) except: pass if addy: self.cash.text = addy.to_full_string(Address.FMT_CASHADDR) self.legacy.text = addy.to_full_string(Address.FMT_LEGACY) self.cpyCashBut.enabled = True self.cpyLegBut.enabled = True self.qrButShowCash.enabled = True self.qrButShowLegacy.enabled = True return True return False
def onOk(self) -> None: #print("On OK...") address_str = cleanup_address_remove_colon(self.address.text) name = str(self.name.text).strip() if not Address.is_valid(address_str): gui.ElectrumGui.gui.show_error(_("Invalid Address"), title=self.title) return if not name: gui.ElectrumGui.gui.show_error(_("Name is empty"), title=self.title) return def doCB() -> None: cb = utils.get_callback(self, 'on_ok') if callable(cb): entry = None if name and address_str and Address.is_valid(address_str): address = Address.from_string(address_str) entry = ContactsEntry(name, address, address_str) cb(entry) self.autorelease() self.retain() self.presentingViewController.dismissViewControllerAnimated_completion_( True, doCB)
def __init__(self, addresses, initial_tx=None, v=0, data=None): Contract.__init__(self, addresses, initial_tx, v) self.participants = 3 self.redeemscript_v1 = joinbytes([ len(addresses[0].hash160), addresses[0].hash160, len(addresses[1].hash160), addresses[1].hash160, len(addresses[2].hash160), addresses[2].hash160, Op.OP_6, Op.OP_PICK, Op.OP_HASH160, Op.OP_6, Op.OP_PICK, Op.OP_HASH160, Op.OP_OVER, Op.OP_5, Op.OP_PICK, Op.OP_EQUAL, Op.OP_2, Op.OP_PICK, Op.OP_5, Op.OP_PICK, Op.OP_EQUAL, Op.OP_BOOLOR, Op.OP_VERIFY, Op.OP_DUP, Op.OP_3, Op.OP_PICK, Op.OP_EQUAL, Op.OP_OVER, Op.OP_5, Op.OP_PICK, Op.OP_EQUAL, Op.OP_BOOLOR, Op.OP_VERIFY, Op.OP_2DUP, Op.OP_EQUAL, Op.OP_NOT, Op.OP_VERIFY, Op.OP_6, Op.OP_PICK, Op.OP_9, Op.OP_PICK, Op.OP_CHECKSIGVERIFY, Op.OP_5, Op.OP_PICK, Op.OP_8, Op.OP_PICK, Op.OP_CHECKSIG, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP, Op.OP_NIP ]) self.redeemscript = self.redeemscript_v1 self.set_version(v) self.address = Address.from_multisig_script(self.redeemscript) data1 = self.address.to_ui_string() + ' ' + str(self.version) self.op_return = joinbytes( [Op.OP_RETURN, 4, b'>sh\x00', len(data1), data1.encode('utf8')])
def do_sign(self): password = self._get_password() address = self.address_e.text().strip() message = self.message_e.toPlainText().strip() try: addr = Address.from_string(address) except Exception: self.show_message(_(f"Invalid {CURRENCY} address.")) return if addr.kind != addr.ADDR_P2PKH: msg_sign = (_( "Signing with an address actually means signing with the corresponding " "private key, and verifying with the corresponding public key. The " "address you have entered does not have a unique public key, so these " "operations cannot be performed.") + "\n\n" + _(f"The operation is undefined. Not just in " f"{PROJECT_NAME}, but in general.")) self.show_message( _("Cannot sign messages with this type of address.") + "\n\n" + msg_sign) return if self.wallet.is_watching_only(): self.show_message(_("This is a watching-only wallet.")) return if not self.wallet.is_mine(addr): self.show_message(_("Address not in wallet.")) return task = partial(self.wallet.sign_message, addr, message, password, self.get_sigtype()) def show_signed_message(sig): self.signature_e.setText(base64.b64encode(sig).decode("ascii")) self.wallet.thread.add(task, on_success=show_signed_message)
def __init__(self, **kwargs): # initialize variables self._clipboard = Clipboard self.info_bubble = None self.nfcscanner = None self.tabs = None self.is_exit = False self.wallet = None App.__init__(self) #, **kwargs) title = _('Electron-Cash App') self.electrum_config = config = kwargs.get('config', None) self.language = config.get('language', 'en') self.network = network = kwargs.get('network', None) if self.network: self.num_blocks = self.network.get_local_height() self.num_nodes = len(self.network.get_interfaces()) host, port, protocol, proxy_config, auto_connect = self.network.get_parameters( ) self.server_host = host self.server_port = port self.auto_connect = auto_connect self.proxy_config = proxy_config if proxy_config else {} self.plugins = kwargs.get('plugins', []) self.gui_object = kwargs.get('gui_object', None) self.daemon = self.gui_object.daemon self.fx = self.daemon.fx self.use_change = config.get('use_change', True) self.use_cashaddr = config.get('use_cashaddr', True) Address.show_cashaddr(self.use_cashaddr) self.use_unconfirmed = not config.get('confirmed_only', False) # create triggers so as to minimize updation a max of 2 times a sec self._trigger_update_wallet = Clock.create_trigger( self.update_wallet, .5) self._trigger_update_status = Clock.create_trigger( self.update_status, .5) self._trigger_update_history = Clock.create_trigger( self.update_history, .5) self._trigger_update_interfaces = Clock.create_trigger( self.update_interfaces, .5) # cached dialogs self._settings_dialog = None self._password_dialog = None