Merge remote-tracking branch 'upstream/master' into upstreamMerge

This commit is contained in:
Vivek Teega 2019-01-03 18:46:09 +05:30
commit 8702ed096b
28 changed files with 94 additions and 47 deletions

View File

@ -1,3 +1,11 @@
# Release 3.3.2 - (December 21, 2018)
* Fix Qt history export bug
* Improve network timeouts
* Prepend server transaction_broadcast error messages with
explanatory message. Render error messages as plain text.
# Release 3.3.1 - (December 20, 2018) # Release 3.3.1 - (December 20, 2018)
* Qt: Fix invoices tab crash (#4941) * Qt: Fix invoices tab crash (#4941)

@ -1 +1 @@
Subproject commit bce0d7a427ecf2106bf4d1ec56feb4067a50b234 Subproject commit d586021ba0d4820d6587cff000756b3d035d4f08

View File

@ -109,6 +109,13 @@ class ElectrumWindow(App):
def toggle_oneserver(self, x): def toggle_oneserver(self, x):
self.oneserver = not self.oneserver self.oneserver = not self.oneserver
proxy_str = StringProperty('')
def update_proxy_str(self, proxy: dict):
mode = proxy.get('mode')
host = proxy.get('host')
port = proxy.get('port')
self.proxy_str = (host + ':' + port) if mode else _('None')
def choose_server_dialog(self, popup): def choose_server_dialog(self, popup):
from .uix.dialogs.choice_dialog import ChoiceDialog from .uix.dialogs.choice_dialog import ChoiceDialog
protocol = 's' protocol = 's'
@ -288,6 +295,7 @@ class ElectrumWindow(App):
self.auto_connect = net_params.auto_connect self.auto_connect = net_params.auto_connect
self.oneserver = net_params.oneserver self.oneserver = net_params.oneserver
self.proxy_config = net_params.proxy if net_params.proxy else {} self.proxy_config = net_params.proxy if net_params.proxy else {}
self.update_proxy_str(self.proxy_config)
self.plugins = kwargs.get('plugins', []) self.plugins = kwargs.get('plugins', [])
self.gui_object = kwargs.get('gui_object', None) self.gui_object = kwargs.get('gui_object', None)
@ -667,6 +675,7 @@ class ElectrumWindow(App):
self.tabs = self.root.ids['tabs'] self.tabs = self.root.ids['tabs']
def update_interfaces(self, dt): def update_interfaces(self, dt):
net_params = self.network.get_parameters()
self.num_nodes = len(self.network.get_interfaces()) self.num_nodes = len(self.network.get_interfaces())
self.num_chains = len(self.network.get_blockchains()) self.num_chains = len(self.network.get_blockchains())
chain = self.network.blockchain() chain = self.network.blockchain()
@ -675,6 +684,10 @@ class ElectrumWindow(App):
interface = self.network.interface interface = self.network.interface
if interface: if interface:
self.server_host = interface.host self.server_host = interface.host
else:
self.server_host = str(net_params.host) + ' (connecting...)'
self.proxy_config = net_params.proxy or {}
self.update_proxy_str(self.proxy_config)
def on_network_event(self, event, *args): def on_network_event(self, event, *args):
Logger.info('network event: '+ event) Logger.info('network event: '+ event)
@ -924,8 +937,11 @@ class ElectrumWindow(App):
self.wallet.invoices.save() self.wallet.invoices.save()
self.update_tab('invoices') self.update_tab('invoices')
else: else:
msg = msg[:500] if msg else _('There was an error broadcasting the transaction.') display_msg = _('The server returned an error when broadcasting the transaction.')
self.show_error(msg) if msg:
display_msg += '\n' + msg
display_msg = display_msg[:500]
self.show_error(display_msg)
if self.network and self.network.is_connected(): if self.network and self.network.is_connected():
self.show_info(_('Sending')) self.show_info(_('Sending'))

View File

@ -24,10 +24,7 @@ Popup:
CardSeparator CardSeparator
SettingsItem: SettingsItem:
proxy: app.proxy_config.get('mode') title: _("Proxy") + ': ' + app.proxy_str
host: app.proxy_config.get('host')
port: app.proxy_config.get('port')
title: _("Proxy") + ': ' + ((self.host +':' + self.port) if self.proxy else _('None'))
description: _('Proxy configuration') description: _('Proxy configuration')
action: lambda x: app.popup_dialog('proxy') action: lambda x: app.popup_dialog('proxy')

View File

@ -73,5 +73,4 @@ Popup:
if proxy['mode']=='none': proxy = None if proxy['mode']=='none': proxy = None
net_params = net_params._replace(proxy=proxy) net_params = net_params._replace(proxy=proxy)
app.network.run_from_another_thread(app.network.set_parameters(net_params)) app.network.run_from_another_thread(app.network.set_parameters(net_params))
app.proxy_config = proxy if proxy else {}
nd.dismiss() nd.dismiss()

View File

@ -23,7 +23,7 @@ Popup:
height: '36dp' height: '36dp'
size_hint_x: 3 size_hint_x: 3
size_hint_y: None size_hint_y: None
text: app.server_host text: app.network.get_parameters().host
Label: Label:
height: '36dp' height: '36dp'
size_hint_x: 1 size_hint_x: 1
@ -36,7 +36,7 @@ Popup:
height: '36dp' height: '36dp'
size_hint_x: 3 size_hint_x: 3
size_hint_y: None size_hint_y: None
text: app.server_port text: app.network.get_parameters().port
Widget Widget
Button: Button:
id: chooser id: chooser

View File

@ -152,8 +152,11 @@ class AddressList(MyTreeView):
is_multisig = isinstance(self.wallet, Multisig_Wallet) is_multisig = isinstance(self.wallet, Multisig_Wallet)
can_delete = self.wallet.can_delete_address() can_delete = self.wallet.can_delete_address()
selected = self.selected_in_column(1) selected = self.selected_in_column(1)
if not selected:
return
multi_select = len(selected) > 1 multi_select = len(selected) > 1
addrs = [self.model().itemFromIndex(item).text() for item in selected] addrs = [self.model().itemFromIndex(item).text() for item in selected]
menu = QMenu()
if not multi_select: if not multi_select:
idx = self.indexAt(position) idx = self.indexAt(position)
col = idx.column() col = idx.column()
@ -162,8 +165,6 @@ class AddressList(MyTreeView):
return return
addr = addrs[0] addr = addrs[0]
menu = QMenu()
if not multi_select:
addr_column_title = self.model().horizontalHeaderItem(2).text() addr_column_title = self.model().horizontalHeaderItem(2).text()
addr_idx = idx.sibling(idx.row(), 2) addr_idx = idx.sibling(idx.row(), 2)

View File

@ -57,7 +57,8 @@ class Exception_Window(BaseCrashReporter, QWidget, MessageBoxMixin):
collapse_info = QPushButton(_("Show report contents")) collapse_info = QPushButton(_("Show report contents"))
collapse_info.clicked.connect( collapse_info.clicked.connect(
lambda: self.msg_box(QMessageBox.NoIcon, lambda: self.msg_box(QMessageBox.NoIcon,
self, _("Report contents"), self.get_report_string())) self, _("Report contents"), self.get_report_string(),
rich_text=True))
main_box.addWidget(collapse_info) main_box.addWidget(collapse_info)

View File

@ -89,6 +89,7 @@ class HistoryModel(QAbstractItemModel, PrintError):
self.view = None # type: HistoryList self.view = None # type: HistoryList
self.transactions = OrderedDictWithIndex() self.transactions = OrderedDictWithIndex()
self.tx_status_cache = {} # type: Dict[str, Tuple[int, str]] self.tx_status_cache = {} # type: Dict[str, Tuple[int, str]]
self.summary = None
def set_view(self, history_list: 'HistoryList'): def set_view(self, history_list: 'HistoryList'):
# FIXME HistoryModel and HistoryList mutually depend on each other. # FIXME HistoryModel and HistoryList mutually depend on each other.

View File

@ -86,6 +86,7 @@ class StatusBarButton(QPushButton):
self.clicked.connect(self.onPress) self.clicked.connect(self.onPress)
self.func = func self.func = func
self.setIconSize(QSize(25,25)) self.setIconSize(QSize(25,25))
self.setCursor(QCursor(Qt.PointingHandCursor))
def onPress(self, checked=False): def onPress(self, checked=False):
'''Drops the unwanted PyQt5 "checked" argument''' '''Drops the unwanted PyQt5 "checked" argument'''
@ -610,7 +611,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
_("Before reporting a bug, upgrade to the most recent version of Electrum (latest release or git HEAD), and include the version number in your report."), _("Before reporting a bug, upgrade to the most recent version of Electrum (latest release or git HEAD), and include the version number in your report."),
_("Try to explain not only what the bug is, but how it occurs.") _("Try to explain not only what the bug is, but how it occurs.")
]) ])
self.show_message(msg, title="Electrum - " + _("Reporting Bugs")) self.show_message(msg, title="Electrum - " + _("Reporting Bugs"), rich_text=True)
def notify_transactions(self): def notify_transactions(self):
if self.tx_notification_queue.qsize() == 0: if self.tx_notification_queue.qsize() == 0:
@ -1697,7 +1698,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.invoice_list.update() self.invoice_list.update()
self.do_clear() self.do_clear()
else: else:
parent.show_error(msg) display_msg = _('The server returned an error when broadcasting the transaction.')
if msg:
display_msg += '\n' + msg
parent.show_error(display_msg)
WaitingDialog(self, _('Broadcasting transaction...'), WaitingDialog(self, _('Broadcasting transaction...'),
broadcast_thread, broadcast_done, self.on_error) broadcast_thread, broadcast_done, self.on_error)

View File

@ -190,24 +190,24 @@ class MessageBoxMixin(object):
parent, title or '', parent, title or '',
msg, buttons=Yes|No, defaultButton=No) == Yes msg, buttons=Yes|No, defaultButton=No) == Yes
def show_warning(self, msg, parent=None, title=None): def show_warning(self, msg, parent=None, title=None, **kwargs):
return self.msg_box(QMessageBox.Warning, parent, return self.msg_box(QMessageBox.Warning, parent,
title or _('Warning'), msg) title or _('Warning'), msg, **kwargs)
def show_error(self, msg, parent=None): def show_error(self, msg, parent=None, **kwargs):
return self.msg_box(QMessageBox.Warning, parent, return self.msg_box(QMessageBox.Warning, parent,
_('Error'), msg) _('Error'), msg, **kwargs)
def show_critical(self, msg, parent=None, title=None): def show_critical(self, msg, parent=None, title=None, **kwargs):
return self.msg_box(QMessageBox.Critical, parent, return self.msg_box(QMessageBox.Critical, parent,
title or _('Critical Error'), msg) title or _('Critical Error'), msg, **kwargs)
def show_message(self, msg, parent=None, title=None): def show_message(self, msg, parent=None, title=None, **kwargs):
return self.msg_box(QMessageBox.Information, parent, return self.msg_box(QMessageBox.Information, parent,
title or _('Information'), msg) title or _('Information'), msg, **kwargs)
def msg_box(self, icon, parent, title, text, buttons=QMessageBox.Ok, def msg_box(self, icon, parent, title, text, buttons=QMessageBox.Ok,
defaultButton=QMessageBox.NoButton): defaultButton=QMessageBox.NoButton, rich_text=False):
parent = parent or self.top_level_window() parent = parent or self.top_level_window()
if type(icon) is QPixmap: if type(icon) is QPixmap:
d = QMessageBox(QMessageBox.Information, title, str(text), buttons, parent) d = QMessageBox(QMessageBox.Information, title, str(text), buttons, parent)
@ -216,7 +216,12 @@ class MessageBoxMixin(object):
d = QMessageBox(icon, title, str(text), buttons, parent) d = QMessageBox(icon, title, str(text), buttons, parent)
d.setWindowModality(Qt.WindowModal) d.setWindowModality(Qt.WindowModal)
d.setDefaultButton(defaultButton) d.setDefaultButton(defaultButton)
d.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse) if rich_text:
d.setTextInteractionFlags(Qt.TextSelectableByMouse| Qt.LinksAccessibleByMouse)
d.setTextFormat(Qt.RichText)
else:
d.setTextInteractionFlags(Qt.TextSelectableByMouse)
d.setTextFormat(Qt.PlainText)
return d.exec_() return d.exec_()
class WindowModalDialog(QDialog, MessageBoxMixin): class WindowModalDialog(QDialog, MessageBoxMixin):

View File

@ -206,7 +206,9 @@ class ElectrumGui:
try: try:
self.network.run_from_another_thread(self.network.broadcast_transaction(tx)) self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
except Exception as e: except Exception as e:
print(repr(e)) display_msg = _('The server returned an error when broadcasting the transaction.')
display_msg += '\n' + repr(e)
print(display_msg)
else: else:
print(_('Payment sent.')) print(_('Payment sent.'))
#self.do_clear() #self.do_clear()

View File

@ -15,7 +15,7 @@ from electrum.storage import WalletStorage
from electrum.network import NetworkParameters from electrum.network import NetworkParameters
from electrum.interface import deserialize_server from electrum.interface import deserialize_server
_ = lambda x:x _ = lambda x:x # i18n
class ElectrumGui: class ElectrumGui:
@ -370,7 +370,9 @@ class ElectrumGui:
try: try:
self.network.run_from_another_thread(self.network.broadcast_transaction(tx)) self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
except Exception as e: except Exception as e:
self.show_message(repr(e)) display_msg = _('The server returned an error when broadcasting the transaction.')
display_msg += '\n' + repr(e)
self.show_message(display_msg)
else: else:
self.show_message(_('Payment sent.')) self.show_message(_('Payment sent.'))
self.do_clear() self.do_clear()

View File

@ -737,6 +737,7 @@ class Network(PrintError):
timeout = self.get_network_timeout_seconds(NetworkTimeout.Urgent) timeout = self.get_network_timeout_seconds(NetworkTimeout.Urgent)
out = await self.interface.session.send_request('blockchain.transaction.broadcast', [str(tx)], timeout=timeout) out = await self.interface.session.send_request('blockchain.transaction.broadcast', [str(tx)], timeout=timeout)
if out != tx.txid(): if out != tx.txid():
# note: this is untrusted input from the server
raise Exception(out) raise Exception(out)
return out # txid return out # txid

View File

@ -26,7 +26,7 @@
import threading import threading
from PyQt5.Qt import QVBoxLayout, QLabel from PyQt5.QtWidgets import QVBoxLayout, QLabel
from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE from electrum.gui.qt.password_dialog import PasswordLayout, PW_PASSPHRASE
from electrum.gui.qt.util import * from electrum.gui.qt.util import *

View File

@ -1,9 +1,9 @@
from functools import partial from functools import partial
import threading import threading
from PyQt5.Qt import Qt from PyQt5.QtCore import Qt
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
from PyQt5.Qt import QVBoxLayout, QLabel from PyQt5.QtWidgets import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _

View File

@ -7,7 +7,7 @@ from binascii import hexlify, unhexlify
import websocket import websocket
from PyQt5.Qt import QDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel from PyQt5.QtWidgets import QDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel
import PyQt5.QtCore as QtCore import PyQt5.QtCore as QtCore
from PyQt5.QtWidgets import * from PyQt5.QtWidgets import *

View File

@ -170,19 +170,22 @@ class Plugin(RevealerPlugin):
code_id = self.versioned_seed.checksum code_id = self.versioned_seed.checksum
dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, version, code_id), dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, version, code_id),
"<b>", self.get_path_to_revealer_file(), "</b>", "<br/>", "<b>", self.get_path_to_revealer_file(), "</b>", "<br/>",
"<br/>", "<b>", _("Always check you backups.")])) "<br/>", "<b>", _("Always check you backups.")]),
rich_text=True)
dialog.close() dialog.close()
def ext_warning(self, dialog): def ext_warning(self, dialog):
dialog.show_message(''.join(["<b>",_("Warning"), ": </b>", dialog.show_message(''.join(["<b>",_("Warning"), ": </b>",
_("your seed extension will <b>not</b> be included in the encrypted backup.")])) _("your seed extension will <b>not</b> be included in the encrypted backup.")]),
rich_text=True)
dialog.close() dialog.close()
def bdone(self, dialog): def bdone(self, dialog):
version = self.versioned_seed.version version = self.versioned_seed.version
code_id = self.versioned_seed.checksum code_id = self.versioned_seed.checksum
dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(version, code_id), dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(version, code_id),
"<br/>","<b>", self.get_path_to_revealer_file(), '</b>'])) "<br/>","<b>", self.get_path_to_revealer_file(), '</b>']),
rich_text=True)
def customtxt_limits(self): def customtxt_limits(self):
@ -208,7 +211,8 @@ class Plugin(RevealerPlugin):
.format(warning=_("Warning"), .format(warning=_("Warning"),
ver0=_("Revealers starting with 0 are not secure due to a vulnerability."), ver0=_("Revealers starting with 0 are not secure due to a vulnerability."),
url=_("More info at: {}").format(f'<a href="{link}">{link}</a>'), url=_("More info at: {}").format(f'<a href="{link}">{link}</a>'),
risk=_("Proceed at your own risk."))) risk=_("Proceed at your own risk.")),
rich_text=True)
def cypherseed_dialog(self, window): def cypherseed_dialog(self, window):
self.warn_old_revealer() self.warn_old_revealer()

View File

@ -1,9 +1,9 @@
from functools import partial from functools import partial
import threading import threading
from PyQt5.Qt import Qt from PyQt5.QtCore import Qt
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
from PyQt5.Qt import QVBoxLayout, QLabel from PyQt5.QtWidgets import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _

View File

@ -1,9 +1,9 @@
from functools import partial from functools import partial
import threading import threading
from PyQt5.Qt import Qt from PyQt5.QtCore import Qt
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
from PyQt5.Qt import QVBoxLayout, QLabel from PyQt5.QtWidgets import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _

View File

@ -45,7 +45,7 @@ from electrum.mnemonic import Mnemonic
from electrum.wallet import Multisig_Wallet, Deterministic_Wallet from electrum.wallet import Multisig_Wallet, Deterministic_Wallet
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin, hook from electrum.plugin import BasePlugin, hook
from electrum.util import NotEnoughFunds from electrum.util import NotEnoughFunds, UserFacingException
from electrum.storage import STO_EV_USER_PW from electrum.storage import STO_EV_USER_PW
from electrum.network import Network from electrum.network import Network
@ -319,7 +319,13 @@ class Wallet_2fa(Multisig_Wallet):
otp = int(otp) otp = int(otp)
long_user_id, short_id = self.get_user_id() long_user_id, short_id = self.get_user_id()
raw_tx = tx.serialize() raw_tx = tx.serialize()
r = server.sign(short_id, raw_tx, otp) try:
r = server.sign(short_id, raw_tx, otp)
except TrustedCoinException as e:
if e.status_code == 400: # invalid OTP
raise UserFacingException(_('Invalid one-time password.')) from e
else:
raise
if r: if r:
raw_tx = r.get('transaction') raw_tx = r.get('transaction')
tx.update(raw_tx) tx.update(raw_tx)

View File

@ -1,5 +1,5 @@
ELECTRUM_VERSION = '3.3.1' # version of the client package ELECTRUM_VERSION = '3.3.2' # version of the client package
APK_VERSION = '3.3.1.0' # read by buildozer.spec APK_VERSION = '3.3.2.0' # read by buildozer.spec
PROTOCOL_VERSION = '1.4' # protocol version requested PROTOCOL_VERSION = '1.4' # protocol version requested

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
icons/clock5.pdn Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB