Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
pooler committed Oct 5, 2017
2 parents a649929 + e299df7 commit f2ac3ce
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 135 deletions.
1 change: 1 addition & 0 deletions gui/qt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def __init__(self, config, daemon, plugins):
self.tray.show()
self.app.new_window_signal.connect(self.start_new_window)
run_hook('init_qt', self)
ColorScheme.update_from_widget(QWidget())

def build_tray_menu(self):
# Avoid immediate GC of old menu when window closed via its action
Expand Down
3 changes: 2 additions & 1 deletion gui/qt/address_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def __init__(self, parent, address):
vbox.addWidget(QLabel(_("Address:")))
self.addr_e = ButtonsLineEdit(self.address)
self.addr_e.addCopyButton(self.app)
self.addr_e.addButton(":icons/qrcode.png", self.show_qr, _("Show QR Code"))
icon = ":icons/qrcode_white.png" if ColorScheme.dark_scheme else ":icons/qrcode.png"
self.addr_e.addButton(icon, self.show_qr, _("Show QR Code"))
self.addr_e.setReadOnly(True)
vbox.addWidget(self.addr_e)

Expand Down
7 changes: 5 additions & 2 deletions gui/qt/address_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def on_update(self):
address_item.setData(0, Qt.UserRole, address)
address_item.setData(0, Qt.UserRole+1, True) # label can be edited
if self.wallet.is_frozen(address):
address_item.setBackground(0, QColor('lightblue'))
address_item.setBackground(0, ColorScheme.BLUE.as_color(True))
if self.wallet.is_beyond_limit(address, is_change):
address_item.setBackground(0, QColor('red'))
address_item.setBackground(0, ColorScheme.RED.as_color(True))
if is_used:
if not used_flag:
seq_item.insertChild(0, used_item)
Expand Down Expand Up @@ -158,3 +158,6 @@ def create_menu(self, position):
run_hook('receive_menu', menu, addrs, self.wallet)
menu.exec_(self.viewport().mapToGlobal(position))

def on_permit_edit(self, item, column):
# labels for headings, e.g. "receiving" or "used" should not be editable
return item.childCount() == 0
49 changes: 24 additions & 25 deletions gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ def connect_fields(self, window, btc_e, fiat_e, fee_e):
def edit_changed(edit):
if edit.follows:
return
edit.setStyleSheet(BLACK_FG)
edit.setStyleSheet(ColorScheme.DEFAULT.as_stylesheet())
fiat_e.is_last_edited = (edit == fiat_e)
amount = edit.get_amount()
rate = self.fx.exchange_rate() if self.fx else None
Expand All @@ -655,15 +655,15 @@ def edit_changed(edit):
if edit is fiat_e:
btc_e.follows = True
btc_e.setAmount(int(amount / Decimal(rate) * COIN))
btc_e.setStyleSheet(BLUE_FG)
btc_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
btc_e.follows = False
if fee_e:
window.update_fee()
else:
fiat_e.follows = True
fiat_e.setText(self.fx.ccy_amount_str(
amount * Decimal(rate) / COIN, False))
fiat_e.setStyleSheet(BLUE_FG)
fiat_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
fiat_e.follows = False

btc_e.follows = False
Expand Down Expand Up @@ -916,7 +916,7 @@ def export_payment_request(self, addr):
fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70")
if fileName:
with open(fileName, "wb+") as f:
f.write(str(pr))
f.write(util.to_bytes(pr))
self.show_message(_("Request saved successfully"))
self.saved = True

Expand Down Expand Up @@ -1111,22 +1111,22 @@ def reset_max(t):
def entry_changed():
text = ""
if self.not_enough_funds:
amt_color, fee_color = RED_FG, RED_FG
amt_color, fee_color = ColorScheme.RED, ColorScheme.RED
text = _( "Not enough funds" )
c, u, x = self.wallet.get_frozen_balance()
if c+u+x:
text += ' (' + self.format_amount(c+u+x).strip() + ' ' + self.base_unit() + ' ' +_("are frozen") + ')'

elif self.fee_e.isModified():
amt_color, fee_color = BLACK_FG, BLACK_FG
amt_color, fee_color = ColorScheme.DEFAULT, ColorScheme.DEFAULT
elif self.amount_e.isModified():
amt_color, fee_color = BLACK_FG, BLUE_FG
amt_color, fee_color = ColorScheme.DEFAULT, ColorScheme.BLUE
else:
amt_color, fee_color = BLUE_FG, BLUE_FG
amt_color, fee_color = ColorScheme.BLUE, ColorScheme.BLUE

self.statusBar().showMessage(text)
self.amount_e.setStyleSheet(amt_color)
self.fee_e.setStyleSheet(fee_color)
self.amount_e.setStyleSheet(amt_color.as_stylesheet())
self.fee_e.setStyleSheet(fee_color.as_stylesheet())

self.amount_e.textChanged.connect(entry_changed)
self.fee_e.textChanged.connect(entry_changed)
Expand Down Expand Up @@ -1898,7 +1898,8 @@ def do_sign(self, address, message, signature, password):
if not bitcoin.is_address(address):
self.show_message('Invalid Litecoin address.')
return
if not bitcoin.is_p2pkh(address):
txin_type = self.wallet.get_txin_type(address)
if txin_type not in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
self.show_message('Cannot sign messages with this type of address.' + '\n\n' + self.msg_sign)
return
if not self.wallet.is_mine(address):
Expand All @@ -1912,13 +1913,10 @@ def show_signed_message(sig):

def do_verify(self, address, message, signature):
address = address.text().strip()
message = message.toPlainText().strip().encode('utf8')
message = message.toPlainText().strip().encode('utf-8')
if not bitcoin.is_address(address):
self.show_message('Invalid Litecoin address.')
return
if not bitcoin.is_p2pkh(address):
self.show_message('Cannot verify messages with this type of address.' + '\n\n' + self.msg_sign)
return
try:
# This can throw on invalid base64
sig = base64.b64decode(str(signature.toPlainText()))
Expand All @@ -1932,7 +1930,7 @@ def do_verify(self, address, message, signature):

def sign_verify_message(self, address=''):
d = WindowModalDialog(self, _('Sign/verify Message'))
d.setMinimumSize(410, 290)
d.setMinimumSize(610, 290)

layout = QGridLayout(d)

Expand Down Expand Up @@ -1971,11 +1969,11 @@ def sign_verify_message(self, address=''):
def do_decrypt(self, message_e, pubkey_e, encrypted_e, password):
cyphertext = encrypted_e.toPlainText()
task = partial(self.wallet.decrypt_message, pubkey_e.text(), cyphertext, password)
self.wallet.thread.add(task, on_success=lambda text: message_e.setText(text.decode('utf8')))
self.wallet.thread.add(task, on_success=lambda text: message_e.setText(text.decode('utf-8')))

def do_encrypt(self, message_e, pubkey_e, encrypted_e):
message = message_e.toPlainText()
message = message.encode('utf8')
message = message.encode('utf-8')
try:
encrypted = bitcoin.encrypt_message(message, pubkey_e.text())
encrypted_e.setText(encrypted.decode('ascii'))
Expand Down Expand Up @@ -2051,8 +2049,8 @@ def read_tx_from_qrcode(self):
self.pay_to_URI(data)
return
# else if the user scanned an offline signed tx
# transactions are binary, but qrcode seems to return utf8...
data = data.decode('utf8')
# transactions are binary, but qrcode seems to return utf-8...
data = data.decode('utf-8')
z = bitcoin.base_decode(data, length=None, base=43)
data = bh2u(''.join(chr(ord(b)) for b in z))
tx = self.tx_from_text(data)
Expand Down Expand Up @@ -2134,7 +2132,8 @@ def privkeys_thread():
time.sleep(0.1)
if done:
break
private_keys[addr] = "\n".join(self.wallet.get_private_key(addr, password))
privkey = self.wallet.export_private_key(addr, password)[0]
private_keys[addr] = privkey
self.computing_privkeys_signal.emit()
self.show_privkeys_signal.emit()

Expand Down Expand Up @@ -2311,7 +2310,7 @@ def get_pk():
return keystore.get_private_keys(text)

f = lambda: button.setEnabled(get_address() is not None and get_pk() is not None)
on_address = lambda text: address_e.setStyleSheet(BLACK_FG if get_address() else RED_FG)
on_address = lambda text: address_e.setStyleSheet((ColorScheme.DEFAULT if get_address() else ColorScheme.RED).as_stylesheet())
keys_e.textChanged.connect(f)
address_e.textChanged.connect(f)
address_e.textChanged.connect(on_address)
Expand Down Expand Up @@ -2474,9 +2473,9 @@ def set_alias_color():
return
if self.alias_info:
alias_addr, alias_name, validated = self.alias_info
alias_e.setStyleSheet(GREEN_BG if validated else RED_BG)
alias_e.setStyleSheet((ColorScheme.GREEN if validated else ColorScheme.RED).as_stylesheet(True))
else:
alias_e.setStyleSheet(RED_BG)
alias_e.setStyleSheet(ColorScheme.RED.as_stylesheet(True))
def on_alias_edit():
alias_e.setStyleSheet("")
alias = str(alias_e.text())
Expand Down Expand Up @@ -2505,7 +2504,7 @@ def on_alias_edit():
SSL_error = None
SSL_id_label = HelpLabel(_('SSL certificate') + ':', msg)
SSL_id_e = QLineEdit(SSL_identity)
SSL_id_e.setStyleSheet(RED_BG if SSL_error else GREEN_BG if SSL_identity else '')
SSL_id_e.setStyleSheet((ColorScheme.RED if SSL_error else ColorScheme.GREEN).as_stylesheet(True) if SSL_identity else '')
if SSL_error:
SSL_id_e.setToolTip(SSL_error)
SSL_id_e.setReadOnly(True)
Expand Down
4 changes: 2 additions & 2 deletions gui/qt/paytoedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ def setFrozen(self, b):
button.setHidden(b)

def setGreen(self):
self.setStyleSheet(util.GREEN_BG)
self.setStyleSheet(util.ColorScheme.GREEN.as_stylesheet(True))

def setExpired(self):
self.setStyleSheet(util.RED_BG)
self.setStyleSheet(util.ColorScheme.RED.as_stylesheet(True))

def parse_address_and_amount(self, line):
x, y = line.split(',')
Expand Down
5 changes: 3 additions & 2 deletions gui/qt/qrtextedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QFileDialog

from .util import ButtonsTextEdit, MessageBoxMixin
from .util import ButtonsTextEdit, MessageBoxMixin, ColorScheme


class ShowQRTextEdit(ButtonsTextEdit):
Expand Down Expand Up @@ -42,7 +42,8 @@ def __init__(self, text=""):
ButtonsTextEdit.__init__(self, text)
self.setReadOnly(0)
self.addButton(":icons/file.png", self.file_input, _("Read file"))
self.addButton(":icons/qrcode.png", self.qr_input, _("Read QR code"))
icon = ":icons/qrcode_white.png" if ColorScheme.dark_scheme else ":icons/qrcode.png"
self.addButton(icon, self.qr_input, _("Read QR code"))
run_hook('scan_text_edit', self)

def file_input(self):
Expand Down
40 changes: 35 additions & 5 deletions gui/qt/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@
else:
MONOSPACE_FONT = 'monospace'

GREEN_BG = "QWidget {background-color:#80ff80;}"
RED_BG = "QWidget {background-color:#ffcccc;}"
RED_FG = "QWidget {color:red;}"
BLUE_FG = "QWidget {color:blue;}"
BLACK_FG = "QWidget {color:black;}"

dialogs = []

Expand Down Expand Up @@ -602,6 +597,41 @@ def stop(self):
self.tasks.put(None)


class ColorSchemeItem:
def __init__(self, fg_color, bg_color):
self.colors = (fg_color, bg_color)

def _get_color(self, background):
return self.colors[(int(background) + int(ColorScheme.dark_scheme)) % 2]

def as_stylesheet(self, background=False):
css_prefix = "background-" if background else ""
color = self._get_color(background)
return "QWidget {{ {}color:{}; }}".format(css_prefix, color)

def as_color(self, background=False):
color = self._get_color(background)
return QColor(color)


class ColorScheme:
dark_scheme = False

GREEN = ColorSchemeItem("#117c11", "#8af296")
RED = ColorSchemeItem("#7c1111", "#f18c8c")
BLUE = ColorSchemeItem("#123b7c", "#8cb3f2")
DEFAULT = ColorSchemeItem("black", "white")

@staticmethod
def has_dark_background(widget):
brightness = sum(widget.palette().color(QPalette.Background).getRgb()[0:3])
return brightness < (255*3/2)

@staticmethod
def update_from_widget(widget):
if ColorScheme.has_dark_background(widget):
ColorScheme.dark_scheme = True

if __name__ == "__main__":
app = QApplication([])
t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))
Expand Down
2 changes: 1 addition & 1 deletion gui/qt/utxo_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def on_update(self):
utxo_item.setFont(4, QFont(MONOSPACE_FONT))
utxo_item.setData(0, Qt.UserRole, name)
if self.wallet.is_frozen(address):
utxo_item.setBackground(0, QColor('lightblue'))
utxo_item.setBackground(0, ColorScheme.BLUE.as_color(True))
self.addChild(utxo_item)

def create_menu(self, position):
Expand Down
1 change: 1 addition & 0 deletions icons.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<file>icons/microphone.png</file>
<file>icons/network.png</file>
<file>icons/qrcode.png</file>
<file>icons/qrcode_white.png</file>
<file>icons/preferences.png</file>
<file>icons/seed.png</file>
<file>icons/status_connected.png</file>
Expand Down
Binary file added icons/qrcode_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 1 addition & 4 deletions lib/base_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,7 @@ def create_keystore(self, seed, passphrase):
self.on_keystore(k)

def on_bip43(self, seed, passphrase, derivation):
k = keystore.BIP32_KeyStore({})
bip32_seed = keystore.bip39_to_seed(seed, passphrase)
t = 'segwit_p2sh' if derivation.startswith("m/49'") else 'standard'
k.add_xprv_from_seed(bip32_seed, t, derivation)
k = keystore.from_bip39_seed(seed, passphrase, derivation)
self.on_keystore(k)

def on_keystore(self, k):
Expand Down
14 changes: 6 additions & 8 deletions lib/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,7 @@ def public_key_from_private_key(pk, compressed):
def address_from_private_key(sec):
txin_type, privkey, compressed = deserialize_privkey(sec)
public_key = public_key_from_private_key(privkey, compressed)
address = pubkey_to_address(txin_type, public_key)
return address
return pubkey_to_address(txin_type, public_key)

def is_segwit_address(addr):
witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr)
Expand Down Expand Up @@ -611,8 +610,11 @@ def verify_message(address, sig, message):
public_key, compressed = pubkey_from_signature(sig, h)
# check public key using the address
pubkey = point_to_ser(public_key.pubkey.point, compressed)
addr = public_key_to_p2pkh(pubkey)
if address != addr:
for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']:
addr = pubkey_to_address(txin_type, bh2u(pubkey))
if address == addr:
break
else:
raise Exception("Bad signature")
# check message
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
Expand All @@ -621,10 +623,6 @@ def verify_message(address, sig, message):
print_error("Verification error: {0}".format(e))
return False

def sign_message_with_wif_privkey(sec, message):
txin_type, privkey, compressed = deserialize_privkey(sec)
key = regenerate_key(privkey)
return key.sign_message(message, compressed)

def encrypt_message(message, pubkey):
return EC_KEY.encrypt_message(message, bfh(pubkey))
Expand Down
4 changes: 2 additions & 2 deletions lib/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,9 @@ def unfreeze(self, address):
def getprivatekeys(self, address, password=None):
"""Get private keys of addresses. You may pass a single wallet address, or a list of wallet addresses."""
if is_address(address):
return self.wallet.get_private_key(address, password)
return self.wallet.export_private_key(address, password)[0]
domain = address
return [self.wallet.get_private_key(address, password) for address in domain]
return [self.wallet.export_private_key(address, password)[0] for address in domain]

@command('w')
def ismine(self, address):
Expand Down
2 changes: 1 addition & 1 deletion lib/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def run_cmdline(self, config_options):
path = config.get_wallet_path()
wallet = self.wallets.get(path)
if wallet is None:
return {'error': 'Wallet not open. Use "electrum-ltc daemon load_wallet"'}
return {'error': 'Wallet "%s" is not loaded. Use "electrum-ltc daemon load_wallet"'%os.path.basename(path) }
else:
wallet = None
# arguments passed to function
Expand Down
Loading

0 comments on commit f2ac3ce

Please sign in to comment.