Example #1
0
 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)
Example #2
0
 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))
Example #3
0
 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:
         if True:  #with self.lock:
             addrstr = address.to_storage_string()
             k = 'contact_history_%s' % (addrstr)
             storage.put(k, None)
             self.print_error("Deleted %s" % addrstr)
Example #4
0
 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)
Example #5
0
 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
Example #6
0
 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
Example #7
0
 def make_unsigned_transaction(self, amount, fee, all_inputs, outputs,
                               changes):
     ''' make unsigned transaction '''
     dust = self.dust_threshold(
     )  # always 546 for now, but this call is here in case something more sophisticated happens in the future
     coins = {}
     tx_inputs = []
     amounts = {}
     try:
         for player in all_inputs:
             inputs_coins = self.get_coins(all_inputs[player])
             # if there are no coins on input it terminates the process
             if inputs_coins:
                 coins[player] = inputs_coins
             else:
                 return None
     except BaseException as e:
         self.print_error('make_unsigned_transaction:', repr(e))
         return None
     for player, pubkey_utxos in coins.items():
         amounts[player] = 0
         for pubkey, utxos in pubkey_utxos.items():
             for utxo in utxos:
                 utxo['type'] = 'p2pkh'
                 utxo['address'] = Address.from_pubkey(pubkey)
                 utxo['pubkeys'] = [pubkey]
                 utxo['x_pubkeys'] = [pubkey]
                 utxo['prevout_hash'] = utxo['tx_hash']
                 utxo['prevout_n'] = utxo['tx_pos']
                 utxo['signatures'] = [None]
                 utxo['num_sig'] = 1
                 tx_inputs.append(utxo)
                 amounts[player] += utxo['value']
     tx_inputs.sort(key=lambda x: x['prevout_hash'] + str(x["tx_pos"]))
     tx_outputs = [(TYPE_ADDRESS, Address.from_string(output), int(amount))
                   for output in outputs]
     transaction = Transaction.from_io(tx_inputs,
                                       tx_outputs,
                                       sign_schnorr=False)
     tx_changes = [
         (TYPE_ADDRESS, Address.from_string(changes[player]),
          int(amounts[player] - amount - fee)) for player in sorted(changes)
         if Address.is_valid(changes[player]) and int(amounts[player] -
                                                      amount - fee) >= dust
     ]
     transaction.add_outputs(tx_changes)
     return transaction
Example #8
0
    def do_send(self):
        if not Address.is_valid(self.str_recipient):
            print(_('Invalid Bitcoin address'))
            return
        try:
            amount = int(PyDecimal(self.str_amount) * COIN)
        except Exception:
            print(_('Invalid Amount'))
            return
        try:
            fee = int(PyDecimal(self.str_fee) * COIN)
        except Exception:
            print(_('Invalid Fee'))
            return

        if self.wallet.has_password():
            password = self.password_dialog()
            if not password:
                return
        else:
            password = None

        c = ""
        while c != "y":
            c = input("ok to send (y/n)?")
            if c == "n": return

        try:
            tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)],
                                  password, self.config, fee)
        except Exception as e:
            print(str(e))
            return

        if self.str_description:
            self.wallet.labels[tx.txid()] = self.str_description

        print(_("Please wait..."))
        status, msg = self.network.broadcast_transaction(tx)

        if status:
            print(_('Payment sent.'))
        else:
            print(_('Error'))
Example #9
0
 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)
     elif len(link) == 64:  # 64 character txid
         tx = wallet.transactions.get(link)
         if tx:
             parent.show_transaction(tx, tx_desc=wallet.get_label(link))
         else:
             parent.do_process_from_txid(txid=link,
                                         tx_desc=wallet.get_label(link))
         return
Example #10
0
 def get_full_contacts(self, include_pseudo: bool = True) -> List[Contact]:
     ''' Returns all the contacts, with the "My CashAcct" pseudo-contacts
     clobbering dupes of the same type that were manually added.
     Client code should scan for type == 'cashacct' and type == 'cashacct_W' '''
     if include_pseudo:
         # filter out cachaccts that are "Wallet", as they will be added
         # at the end as pseudo contacts if they also appear in real contacts
         real_contacts = [
             contact
             for contact in self.parent.contacts.get_all(nocopy=True)
             if contact.type !=
             'cashacct'  # accept anything that's not cashacct
             or not Address.is_valid(
                 contact.address
             )  # or if it is, it can have invalid address as it's clearly 'not mine"
             or not self.wallet.is_mine(  # or if it's not mine
                 Address.from_string(contact.address))
         ]
         return real_contacts + self._make_wallet_cashacct_pseudo_contacts()
     else:
         return self.parent.contacts.get_all(nocopy=True)
Example #11
0
    def do_send(self):
        if not Address.is_valid(self.str_recipient):
            self.show_message(_('Invalid Bitcoin address'))
            return
        try:
            amount = int(PyDecimal(self.str_amount) * COIN)
        except Exception:
            self.show_message(_('Invalid Amount'))
            return
        try:
            fee = int(PyDecimal(self.str_fee) * COIN)
        except Exception:
            self.show_message(_('Invalid Fee'))
            return

        if self.wallet.has_password():
            password = self.password_dialog()
            if not password:
                return
        else:
            password = None
        try:
            tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)],
                                  password, self.config, fee)
        except Exception as e:
            self.show_message(str(e))
            return

        if self.str_description:
            self.wallet.labels[tx.txid()] = self.str_description

        self.show_message(_("Please wait..."), getchar=False)
        status, msg = self.network.broadcast_transaction(tx)

        if status:
            self.show_message(_('Payment sent.'))
            self.do_clear()
            #self.update_contacts_tab()
        else:
            self.show_message(_('Error'))
Example #12
0
    def create_menu(self, position):
        menu = QMenu()
        selected = self.selectedItems()
        i2c = self._i2c
        ca_unverified = self._get_ca_unverified(include_temp=False)
        if selected:
            names = [item.text(1) for item in selected]
            keys = [i2c(item) for item in selected]
            payable_keys = [k for k in keys if k.type != 'cashacct_T']
            deletable_keys = [k for k in keys if k.type in contact_types]
            needs_verif_keys = [k for k in keys if k in ca_unverified]
            column = self.currentColumn()
            column_title = self.headerItem().text(column)
            column_data = '\n'.join([item.text(column) for item in selected])
            item = self.currentItem()
            typ = i2c(item).type if item else 'unknown'
            ca_info = None
            if item and typ in ('cashacct', 'cashacct_W'):
                ca_info = self.wallet.cashacct.get_verified(i2c(item).name)
                if column == 1 and len(selected) == 1:
                    # hack .. for Cash Accounts just say "Copy Cash Account"
                    column_title = _('Cash Account')
                    if ca_info:
                        column_data = self.wallet.cashacct.fmt_info(ca_info,
                                                                    emoji=True)
            if len(selected) > 1:
                column_title += f" ({len(selected)})"
            menu.addAction(
                _("Copy {}").format(column_title),
                lambda: self.parent.app.clipboard().setText(column_data))
            if item and column in self.editable_columns and self.on_permit_edit(
                    item, column):
                key = item.data(0, self.DataRoles.Contact)
                # this key & find_item business is so we don't hold a reference
                # to the ephemeral item, which may be deleted while the
                # context menu is up.  Accessing the item after on_update runs
                # means the item is deleted and you get a C++ object deleted
                # runtime error.
                menu.addAction(
                    _("Edit {}").format(column_title),
                    lambda: self._on_edit_item(key, column))
            a = menu.addAction(
                _("Pay to"), lambda: self.parent.payto_contacts(payable_keys))
            if needs_verif_keys or not payable_keys:
                a.setDisabled(True)
            a = menu.addAction(
                _("Delete"),
                lambda: self.parent.delete_contacts(deletable_keys))
            if not deletable_keys:
                a.setDisabled(True)
            # Add sign/verify and encrypt/decrypt menu - but only if just 1 thing selected
            if len(keys) == 1 and Address.is_valid(keys[0].address):
                signAddr = Address.from_string(keys[0].address)
                a = menu.addAction(
                    _("Sign/verify message") + "...",
                    lambda: self.parent.sign_verify_message(signAddr))
                if signAddr.kind != Address.ADDR_P2PKH:
                    a.setDisabled(
                        True
                    )  # We only allow this for P2PKH since it makes no sense for P2SH (ambiguous public key)
            URLs = [
                web.BE_URL(self.config, 'addr',
                           Address.from_string(key.address)) for key in keys
                if Address.is_valid(key.address)
            ]
            a = menu.addAction(_("View on block explorer"),
                               lambda: [URL and webopen(URL) for URL in URLs])
            if not any(URLs):
                a.setDisabled(True)
            if ca_info:
                menu.addAction(
                    _("View registration tx..."),
                    lambda: self.parent.do_process_from_txid(
                        txid=ca_info.txid,
                        tx_desc=self.wallet.get_label(ca_info.txid)))
                if typ in ('cashacct_W', 'cashacct'):
                    _contact_d = i2c(item)
                    menu.addAction(
                        _("Details..."),
                        lambda: cashacctqt.cash_account_detail_dialog(
                            self.parent, _contact_d.name))
            menu.addSeparator()

        menu.addAction(self.icon_cashacct,
                       _("Add Contact") + " - " + _("Cash Account"),
                       self.new_cash_account_contact_dialog)
        menu.addAction(self.icon_contacts,
                       _("Add Contact") + " - " + _("Address"),
                       self.parent.new_contact_dialog)
        menu.addSeparator()
        menu.addAction(self.icon_cashacct, _("Register Cash Account..."),
                       self.parent.register_new_cash_account)
        menu.addSeparator()
        menu.addAction(
            QIcon(":icons/import.svg" if not ColorScheme.dark_scheme else
                  ":icons/import_dark_theme.svg"), _("Import file"),
            self.import_contacts)
        if not self.parent.contacts.empty:
            menu.addAction(
                QIcon(":icons/save.svg" if not ColorScheme.dark_scheme else
                      ":icons/save_dark_theme.svg"), _("Export file"),
                self.export_contacts)

        menu.addSeparator()
        a = menu.addAction(_("Show My Cash Accounts"),
                           self.toggle_show_my_cashaccts)
        a.setCheckable(True)
        a.setChecked(self.show_my_cashaccts)

        if ca_unverified:

            def kick_off_verify():
                bnums = set()
                for contact in ca_unverified:
                    tup = self.wallet.cashacct.parse_string(contact.name)
                    if not tup:
                        continue
                    bnums.add(tup[1])  # number
                ret = cashacctqt.verify_multiple_blocks(
                    bnums, self.parent, self.wallet)
                if ret is None:
                    # user cancel
                    return
                verified = ca_unverified - self._get_ca_unverified()
                if not verified:
                    self.parent.show_error(
                        _("Cash Account verification failure"))

            menu.addSeparator()
            num = len(ca_unverified)
            a = menu.addAction(
                self.icon_unverif,
                ngettext("Verify {count} Cash Account",
                         "Verify {count} Cash Accounts",
                         num).format(count=num), kick_off_verify)
            if not self.wallet.network:
                a.setDisabled(True)

        run_hook('create_contact_menu', menu, selected)
        menu.exec_(self.viewport().mapToGlobal(position))