Skip to content

Commit

Permalink
Cash Accounts Implementation + Many Bug Fixes (#1505)
Browse files Browse the repository at this point in the history
Closes #1035 .

The main UI components to it are in:

- **Contacts tab**
- Addresses tab, right click -> Register new.. (to register to a specific address)
- Tools -> Lookup Cash Account
- Receive tab (to register a cash account on the fly to the receive address there in the tab)

What it has:

- *Full verification* -- users are never presented with any results unless they are first SPV verified. They are also warned when a cash account in the contacts tab is no longer verified (due to reorg?) and won't offer it as an option for pay-to.

- Cash Account Registration Auto-Detection™ - Detects cash accounts related to the wallet (that is, registration tx's for the wallet) whenever it "sees" a tx. This can either be a tx that came through the normal wallet "add_transaction" path or if the user explicitly opens a tx, etc.

- The lookup facility is pretty robust and resilient to all sorts of shenanigans.

What it lacks:

- 100% Send tab niceties: Right now the only way to send to a cash account from the send tab is to **first** add it as a contact in the contacts tab.


This last Send Tab bit is a somewhat complex UI flow but I will do it.  I just wanted to get this PR merged in.

As I was working on this I fixed/found/cleaned up a ton of bugs and made the code tighter overall.

This PR includes all of that as well, so I would like to merge it fairly soon and move forward from master with work on Cash Accounts send tab perfection.
  • Loading branch information
cculianu committed Jul 8, 2019
1 parent 478444a commit ba33208
Show file tree
Hide file tree
Showing 41 changed files with 17,881 additions and 11,771 deletions.
33 changes: 30 additions & 3 deletions gui/qt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2012 thomasv@gitorious
Expand Down Expand Up @@ -393,9 +394,10 @@ def on_focus_change(self, ignored, new_focus_widget):
closed.'''
if not new_focus_widget:
return
window = QWidget.window(new_focus_widget) # call base class because some widgets may actually override 'window' with Python attributes.
if isinstance(window, ElectrumWindow):
self._last_active_window = Weak.ref(window)
if isinstance(new_focus_widget, QWidget):
window = QWidget.window(new_focus_widget) # call base class because some widgets may actually override 'window' with Python attributes.
if isinstance(window, ElectrumWindow):
self._last_active_window = Weak.ref(window)

def start_new_window(self, path, uri):
'''Raises the window for the wallet if it is open. Otherwise
Expand Down Expand Up @@ -736,6 +738,31 @@ def set_cashaddr_status_button_hidden(self, b):
self.config.set_key('hide_cashaddr_button', bool(b))
self.cashaddr_status_button_hidden_signal.emit(b)

def test_emoji_fonts(self) -> bool:
''' Returns True if we can render all of the emoji fonts we need,
False otherwise. This needs to be called after the QApplication has
already been instantiated, which is why it's an instance method. Even
though this contains a loop over many characters we need, it ends up
having the following performance: first run is ~130 msec, subsequent
runs are ~10 msec total (on moderate hardware).'''
from electroncash import cashacct
#from . import network_dialog as nd

fontm = QFontMetrics(QFont())
fontm_mono = QFontMetrics(QFont(MONOSPACE_FONT))

emojis = set(cashacct.emoji_list)
# Note the below characters are not emojis, so they do not need to
# be included in this test. The rationale is these characters for
# the network dialog are not 100% critical to the UI, and they also
# are not in the solution we will recommend:install NotoColorEmoji.ttf.
# Uncomment if you wish to also check for these characters, however.
#emojis |= set(ord(ch) for ch in nd.ServerFlag.Symbol + nd.ServerFlag.UnSymbol if ch)
for uval in emojis:
if not fontm.inFontUcs4(uval) or not fontm_mono.inFontUcs4(uval):
return False
return True

def main(self):
try:
self.init_network()
Expand Down
47 changes: 43 additions & 4 deletions gui/qt/address_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from electroncash.i18n import _
from electroncash.address import Address
from electroncash.util import PrintError

from PyQt5.QtCore import *
from PyQt5.QtGui import *
Expand All @@ -33,9 +34,13 @@
from .util import *
from .history_list import HistoryList
from .qrtextedit import ShowQRTextEdit
from . import cashacctqt


class AddressDialog(WindowModalDialog):
class AddressDialog(PrintError, WindowModalDialog):

MIN_WIDTH_NO_FX_HIST = 700
MIN_WIDTH_FX_HIST = MIN_WIDTH_NO_FX_HIST + 75

def __init__(self, parent, address, *, windowParent=None):
assert isinstance(address, Address)
Expand All @@ -48,7 +53,7 @@ def __init__(self, parent, address, *, windowParent=None):
self.app = parent.app
self.saved = True

self.setMinimumWidth(700)
self.setMinimumWidth(self.MIN_WIDTH_FX_HIST if self.parent.fx and self.parent.fx.show_history() else self.MIN_WIDTH_NO_FX_HIST)
vbox = QVBoxLayout()
self.setLayout(vbox)

Expand Down Expand Up @@ -89,6 +94,22 @@ def __init__(self, parent, address, *, windowParent=None):
redeem_e.addCopyButton()
vbox.addWidget(redeem_e)

# Cash Accounts
ca_infos = self.wallet.cashacct.get_cashaccounts(self.get_domain())
vbox.addSpacing(10)
self.cashacct_gb = gb = cashacctqt.InfoGroupBox(self, self.parent, show_addresses=False)
self.update_cash_accounts(ca_infos)
def on_button_click():
item = gb.selectedItem()
if item:
info, ch, mch = item
self.wallet.cashacct.set_address_default(info)
QToolTip.showText(QCursor.pos(), _("Cash Account has been made the default for this address"), gb)
self.parent.address_list.update()
gb.buttonGroup().buttonClicked.connect(on_button_click)
vbox.addWidget(gb)
# /Cash Accounts

vbox.addWidget(QLabel(_("History")))
self.hw = HistoryList(self.parent)
self.hw.get_domain = self.get_domain
Expand Down Expand Up @@ -116,12 +137,30 @@ def disconnect_signals(self):
except TypeError: pass

def got_verified_tx(self, event, args):
if event == 'verified':
self.hw.update_item(*args)
if event == 'verified2' and args[0] is self.wallet:
self.hw.update_item(*args[1:])
elif event in ('ca_verified_tx', 'ca_verification_failed') and args[0] == self.wallet.cashacct and args[1].address == self.address:
self.update_cash_accounts()

def update_addr(self):
self.addr_e.setText(self.address.to_full_ui_string())

def update_cash_accounts(self, ca_infos=None):
gb = self.cashacct_gb
ca_infos = ca_infos or self.wallet.cashacct.get_cashaccounts(self.get_domain())
tups = []
for info in ca_infos:
tups.append((info, self.wallet.cashacct.get_minimal_chash(info.name, info.number, info.collision_hash)))
default = self.wallet.cashacct.get_address_default(ca_infos)
gb.setItems(tups)
if tups:
gb.checkItemWithInfo(default)
if not gb.selectedItem():
gb.checkItemWithInfo(ca_infos[-1])
gb.setHidden(False)
else:
gb.setHidden(True)

def get_domain(self):
return [self.address]

Expand Down
Loading

0 comments on commit ba33208

Please sign in to comment.