Merge remote-tracking branch 'upstream/master' into upstreamMerge
@ -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)
|
||||
|
||||
* Qt: Fix invoices tab crash (#4941)
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit bce0d7a427ecf2106bf4d1ec56feb4067a50b234
|
||||
Subproject commit d586021ba0d4820d6587cff000756b3d035d4f08
|
||||
@ -109,6 +109,13 @@ class ElectrumWindow(App):
|
||||
def toggle_oneserver(self, x):
|
||||
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):
|
||||
from .uix.dialogs.choice_dialog import ChoiceDialog
|
||||
protocol = 's'
|
||||
@ -288,6 +295,7 @@ class ElectrumWindow(App):
|
||||
self.auto_connect = net_params.auto_connect
|
||||
self.oneserver = net_params.oneserver
|
||||
self.proxy_config = net_params.proxy if net_params.proxy else {}
|
||||
self.update_proxy_str(self.proxy_config)
|
||||
|
||||
self.plugins = kwargs.get('plugins', [])
|
||||
self.gui_object = kwargs.get('gui_object', None)
|
||||
@ -667,6 +675,7 @@ class ElectrumWindow(App):
|
||||
self.tabs = self.root.ids['tabs']
|
||||
|
||||
def update_interfaces(self, dt):
|
||||
net_params = self.network.get_parameters()
|
||||
self.num_nodes = len(self.network.get_interfaces())
|
||||
self.num_chains = len(self.network.get_blockchains())
|
||||
chain = self.network.blockchain()
|
||||
@ -675,6 +684,10 @@ class ElectrumWindow(App):
|
||||
interface = self.network.interface
|
||||
if interface:
|
||||
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):
|
||||
Logger.info('network event: '+ event)
|
||||
@ -924,8 +937,11 @@ class ElectrumWindow(App):
|
||||
self.wallet.invoices.save()
|
||||
self.update_tab('invoices')
|
||||
else:
|
||||
msg = msg[:500] if msg else _('There was an error broadcasting the transaction.')
|
||||
self.show_error(msg)
|
||||
display_msg = _('The server returned an error when broadcasting the transaction.')
|
||||
if msg:
|
||||
display_msg += '\n' + msg
|
||||
display_msg = display_msg[:500]
|
||||
self.show_error(display_msg)
|
||||
|
||||
if self.network and self.network.is_connected():
|
||||
self.show_info(_('Sending'))
|
||||
|
||||
@ -24,10 +24,7 @@ Popup:
|
||||
|
||||
CardSeparator
|
||||
SettingsItem:
|
||||
proxy: app.proxy_config.get('mode')
|
||||
host: app.proxy_config.get('host')
|
||||
port: app.proxy_config.get('port')
|
||||
title: _("Proxy") + ': ' + ((self.host +':' + self.port) if self.proxy else _('None'))
|
||||
title: _("Proxy") + ': ' + app.proxy_str
|
||||
description: _('Proxy configuration')
|
||||
action: lambda x: app.popup_dialog('proxy')
|
||||
|
||||
|
||||
@ -73,5 +73,4 @@ Popup:
|
||||
if proxy['mode']=='none': proxy = None
|
||||
net_params = net_params._replace(proxy=proxy)
|
||||
app.network.run_from_another_thread(app.network.set_parameters(net_params))
|
||||
app.proxy_config = proxy if proxy else {}
|
||||
nd.dismiss()
|
||||
|
||||
@ -23,7 +23,7 @@ Popup:
|
||||
height: '36dp'
|
||||
size_hint_x: 3
|
||||
size_hint_y: None
|
||||
text: app.server_host
|
||||
text: app.network.get_parameters().host
|
||||
Label:
|
||||
height: '36dp'
|
||||
size_hint_x: 1
|
||||
@ -36,7 +36,7 @@ Popup:
|
||||
height: '36dp'
|
||||
size_hint_x: 3
|
||||
size_hint_y: None
|
||||
text: app.server_port
|
||||
text: app.network.get_parameters().port
|
||||
Widget
|
||||
Button:
|
||||
id: chooser
|
||||
|
||||
@ -152,8 +152,11 @@ class AddressList(MyTreeView):
|
||||
is_multisig = isinstance(self.wallet, Multisig_Wallet)
|
||||
can_delete = self.wallet.can_delete_address()
|
||||
selected = self.selected_in_column(1)
|
||||
if not selected:
|
||||
return
|
||||
multi_select = len(selected) > 1
|
||||
addrs = [self.model().itemFromIndex(item).text() for item in selected]
|
||||
menu = QMenu()
|
||||
if not multi_select:
|
||||
idx = self.indexAt(position)
|
||||
col = idx.column()
|
||||
@ -162,8 +165,6 @@ class AddressList(MyTreeView):
|
||||
return
|
||||
addr = addrs[0]
|
||||
|
||||
menu = QMenu()
|
||||
if not multi_select:
|
||||
addr_column_title = self.model().horizontalHeaderItem(2).text()
|
||||
addr_idx = idx.sibling(idx.row(), 2)
|
||||
|
||||
|
||||
@ -57,7 +57,8 @@ class Exception_Window(BaseCrashReporter, QWidget, MessageBoxMixin):
|
||||
collapse_info = QPushButton(_("Show report contents"))
|
||||
collapse_info.clicked.connect(
|
||||
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)
|
||||
|
||||
|
||||
@ -89,6 +89,7 @@ class HistoryModel(QAbstractItemModel, PrintError):
|
||||
self.view = None # type: HistoryList
|
||||
self.transactions = OrderedDictWithIndex()
|
||||
self.tx_status_cache = {} # type: Dict[str, Tuple[int, str]]
|
||||
self.summary = None
|
||||
|
||||
def set_view(self, history_list: 'HistoryList'):
|
||||
# FIXME HistoryModel and HistoryList mutually depend on each other.
|
||||
|
||||
@ -86,6 +86,7 @@ class StatusBarButton(QPushButton):
|
||||
self.clicked.connect(self.onPress)
|
||||
self.func = func
|
||||
self.setIconSize(QSize(25,25))
|
||||
self.setCursor(QCursor(Qt.PointingHandCursor))
|
||||
|
||||
def onPress(self, checked=False):
|
||||
'''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."),
|
||||
_("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):
|
||||
if self.tx_notification_queue.qsize() == 0:
|
||||
@ -1697,7 +1698,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||
self.invoice_list.update()
|
||||
self.do_clear()
|
||||
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...'),
|
||||
broadcast_thread, broadcast_done, self.on_error)
|
||||
|
||||
@ -190,24 +190,24 @@ class MessageBoxMixin(object):
|
||||
parent, title or '',
|
||||
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,
|
||||
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,
|
||||
_('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,
|
||||
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,
|
||||
title or _('Information'), msg)
|
||||
title or _('Information'), msg, **kwargs)
|
||||
|
||||
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()
|
||||
if type(icon) is QPixmap:
|
||||
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.setWindowModality(Qt.WindowModal)
|
||||
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_()
|
||||
|
||||
class WindowModalDialog(QDialog, MessageBoxMixin):
|
||||
|
||||
@ -206,7 +206,9 @@ class ElectrumGui:
|
||||
try:
|
||||
self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
|
||||
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:
|
||||
print(_('Payment sent.'))
|
||||
#self.do_clear()
|
||||
|
||||
@ -15,7 +15,7 @@ from electrum.storage import WalletStorage
|
||||
from electrum.network import NetworkParameters
|
||||
from electrum.interface import deserialize_server
|
||||
|
||||
_ = lambda x:x
|
||||
_ = lambda x:x # i18n
|
||||
|
||||
|
||||
class ElectrumGui:
|
||||
@ -370,7 +370,9 @@ class ElectrumGui:
|
||||
try:
|
||||
self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
|
||||
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:
|
||||
self.show_message(_('Payment sent.'))
|
||||
self.do_clear()
|
||||
|
||||
@ -737,6 +737,7 @@ class Network(PrintError):
|
||||
timeout = self.get_network_timeout_seconds(NetworkTimeout.Urgent)
|
||||
out = await self.interface.session.send_request('blockchain.transaction.broadcast', [str(tx)], timeout=timeout)
|
||||
if out != tx.txid():
|
||||
# note: this is untrusted input from the server
|
||||
raise Exception(out)
|
||||
return out # txid
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
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.util import *
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from functools import partial
|
||||
import threading
|
||||
|
||||
from PyQt5.Qt import Qt
|
||||
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.Qt import QVBoxLayout, QLabel
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
||||
|
||||
from electrum.gui.qt.util import *
|
||||
from electrum.i18n import _
|
||||
|
||||
@ -7,7 +7,7 @@ from binascii import hexlify, unhexlify
|
||||
|
||||
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
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
|
||||
@ -170,19 +170,22 @@ class Plugin(RevealerPlugin):
|
||||
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),
|
||||
"<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()
|
||||
|
||||
def ext_warning(self, dialog):
|
||||
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()
|
||||
|
||||
def bdone(self, dialog):
|
||||
version = self.versioned_seed.version
|
||||
code_id = self.versioned_seed.checksum
|
||||
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):
|
||||
@ -208,7 +211,8 @@ class Plugin(RevealerPlugin):
|
||||
.format(warning=_("Warning"),
|
||||
ver0=_("Revealers starting with 0 are not secure due to a vulnerability."),
|
||||
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):
|
||||
self.warn_old_revealer()
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from functools import partial
|
||||
import threading
|
||||
|
||||
from PyQt5.Qt import Qt
|
||||
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.Qt import QVBoxLayout, QLabel
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
||||
|
||||
from electrum.gui.qt.util import *
|
||||
from electrum.i18n import _
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from functools import partial
|
||||
import threading
|
||||
|
||||
from PyQt5.Qt import Qt
|
||||
from PyQt5.Qt import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.Qt import QVBoxLayout, QLabel
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt5.QtWidgets import QVBoxLayout, QLabel
|
||||
|
||||
from electrum.gui.qt.util import *
|
||||
from electrum.i18n import _
|
||||
|
||||
@ -45,7 +45,7 @@ from electrum.mnemonic import Mnemonic
|
||||
from electrum.wallet import Multisig_Wallet, Deterministic_Wallet
|
||||
from electrum.i18n import _
|
||||
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.network import Network
|
||||
|
||||
@ -319,7 +319,13 @@ class Wallet_2fa(Multisig_Wallet):
|
||||
otp = int(otp)
|
||||
long_user_id, short_id = self.get_user_id()
|
||||
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:
|
||||
raw_tx = r.get('transaction')
|
||||
tx.update(raw_tx)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
ELECTRUM_VERSION = '3.3.1' # version of the client package
|
||||
APK_VERSION = '3.3.1.0' # read by buildozer.spec
|
||||
ELECTRUM_VERSION = '3.3.2' # version of the client package
|
||||
APK_VERSION = '3.3.2.0' # read by buildozer.spec
|
||||
|
||||
PROTOCOL_VERSION = '1.4' # protocol version requested
|
||||
|
||||
|
||||
BIN
icons/clock1.png
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 7.4 KiB |
BIN
icons/clock2.png
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 8.3 KiB |
BIN
icons/clock3.png
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 7.1 KiB |
BIN
icons/clock4.png
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 7.3 KiB |
BIN
icons/clock5.pdn
Normal file
BIN
icons/clock5.png
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 9.8 KiB |