integrate electrum-3.0.6 changes

This commit is contained in:
zebra-lucky 2018-04-06 02:42:04 +03:00
parent a3aa9f4015
commit 184a9d68d8
16 changed files with 113 additions and 48 deletions

View File

@ -1,3 +1,7 @@
# Release 3.0.6 :
* Integrate changes from Electrum-3.0.6
# Release 3.0.5 : (Security update)
This is a follow-up to the 3.0.4 release, which did not completely fix

View File

@ -278,7 +278,8 @@ def run_offline_command(config, config_options):
# arguments passed to function
args = [config.get(x) for x in cmd.params]
# decode json arguments
args = list(map(json_decode, args))
if cmdname not in ('setconfig',):
args = list(map(json_decode, args))
# options
kwargs = {}
for x in cmd.options:

View File

@ -923,6 +923,10 @@ class ElectrumWindow(App):
return
if not self.wallet.can_export():
return
key = str(self.wallet.export_private_key(addr, password)[0])
pk_label.data = key
try:
key = str(self.wallet.export_private_key(addr, password)[0])
pk_label.data = key
except InvalidPassword:
self.show_error("Invalid PIN")
return
self.protected(_("Enter your PIN code in order to decrypt your private key"), show_private_key, (addr, pk_label))

View File

@ -93,6 +93,6 @@ class AddressDialog(WindowModalDialog):
def show_qr(self):
text = self.address
try:
self.parent.show_qrcode(text, 'Address')
self.parent.show_qrcode(text, 'Address', parent=self)
except Exception as e:
self.show_message(str(e))

View File

@ -2135,6 +2135,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
self.show_message(_("This is a watching-only wallet"))
return
if isinstance(self.wallet, Multisig_Wallet):
self.show_message(_('WARNING: This is a multi-signature wallet.') + '\n' +
_('It can not be "backed up" by simply exporting these private keys.'))
d = WindowModalDialog(self, _('Private keys'))
d.setMinimumSize(850, 300)
vbox = QVBoxLayout(d)
@ -2160,25 +2164,38 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
private_keys = {}
addresses = self.wallet.get_addresses()
done = False
cancelled = False
def privkeys_thread():
for addr in addresses:
time.sleep(0.1)
if done:
if done or cancelled:
break
privkey = self.wallet.export_private_key(addr, password)[0]
private_keys[addr] = privkey
self.computing_privkeys_signal.emit()
self.computing_privkeys_signal.disconnect()
self.show_privkeys_signal.emit()
if not cancelled:
self.computing_privkeys_signal.disconnect()
self.show_privkeys_signal.emit()
def show_privkeys():
s = "\n".join( map( lambda x: x[0] + "\t"+ x[1], private_keys.items()))
e.setText(s)
b.setEnabled(True)
self.show_privkeys_signal.disconnect()
nonlocal done
done = True
def on_dialog_closed(*args):
nonlocal done
nonlocal cancelled
if not done:
cancelled = True
self.computing_privkeys_signal.disconnect()
self.show_privkeys_signal.disconnect()
self.computing_privkeys_signal.connect(lambda: e.setText("Please wait... %d/%d"%(len(private_keys),len(addresses))))
self.show_privkeys_signal.connect(show_privkeys)
d.finished.connect(on_dialog_closed)
threading.Thread(target=privkeys_thread).start()
if not d.exec_():
@ -2549,7 +2566,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
unit_combo = QComboBox()
unit_combo.addItems(units)
unit_combo.setCurrentIndex(units.index(self.base_unit()))
def on_unit(x):
def on_unit(x, nz):
unit_result = units[unit_combo.currentIndex()]
if self.base_unit() == unit_result:
return
@ -2564,13 +2581,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
else:
raise Exception('Unknown base unit')
self.config.set_key('decimal_point', self.decimal_point, True)
nz.setMaximum(self.decimal_point)
self.history_list.update()
self.request_list.update()
self.address_list.update()
for edit, amount in zip(edits, amounts):
edit.setAmount(amount)
self.update_status()
unit_combo.currentIndexChanged.connect(on_unit)
unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz))
gui_widgets.append((unit_label, unit_combo))
block_explorers = sorted(util.block_explorer_info().keys())

View File

@ -113,8 +113,7 @@ class QRDialog(WindowModalDialog):
def copy_to_clipboard():
p = qscreen.grabWindow(qrw.winId())
p.save(filename, 'png')
QApplication.clipboard().setImage(QImage(filename))
QApplication.clipboard().setPixmap(p)
self.show_message(_("QR code copied to clipboard"))
b = QPushButton(_("Copy"))

View File

@ -85,12 +85,11 @@ class ElectrumGui:
delta = (80 - sum(width) - 4)/3
format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%" \
+ "%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s"
b = 0
messages = []
for item in self.wallet.get_history():
tx_hash, confirmations, value, timestamp, balance = item
if confirmations:
tx_hash, height, conf, timestamp, delta, balance = item
if conf:
try:
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
except Exception:
@ -99,7 +98,7 @@ class ElectrumGui:
time_str = 'unconfirmed'
label = self.wallet.get_label(tx_hash)
messages.append( format_str%( time_str, label, format_satoshis(value, whitespaces=True), format_satoshis(balance, whitespaces=True) ) )
messages.append( format_str%( time_str, label, format_satoshis(delta, whitespaces=True), format_satoshis(balance, whitespaces=True) ) )
self.print_list(messages[::-1], format_str%( _("Date"), _("Description"), _("Amount"), _("Balance")))
@ -149,12 +148,13 @@ class ElectrumGui:
for i, x in enumerate( self.wallet.network.banner.split('\n') ):
print( x )
def print_list(self, list, firstline):
self.maxpos = len(list)
def print_list(self, lst, firstline):
lst = list(lst)
self.maxpos = len(lst)
if not self.maxpos: return
print(firstline)
for i in range(self.maxpos):
msg = list[i] if i < len(list) else ""
msg = lst[i] if i < len(lst) else ""
print(msg)
@ -176,7 +176,7 @@ class ElectrumGui:
print(_('Invalid Fee'))
return
if self.wallet.use_encryption:
if self.wallet.has_password():
password = self.password_dialog()
if not password:
return

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -34,7 +34,7 @@ from functools import wraps
from decimal import Decimal
from .import util
from .util import bfh, bh2u, format_satoshis
from .util import bfh, bh2u, format_satoshis, json_decode
from .import bitcoin
from .bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS
from .i18n import _
@ -151,10 +151,8 @@ class Commands:
@command('')
def setconfig(self, key, value):
"""Set a configuration variable. 'value' may be a string or a Python expression."""
try:
value = ast.literal_eval(value)
except:
pass
if key not in ('rpcuser', 'rpcpassword'):
value = json_decode(value)
self.config.set_key(key, value)
return True

View File

@ -5,6 +5,7 @@ import sys
from threading import Thread
import time
import csv
import decimal
from decimal import Decimal
from .bitcoin import COIN
@ -197,7 +198,11 @@ class FxThread(ThreadJob):
def ccy_amount_str(self, amount, commas):
prec = CCY_PRECISIONS.get(self.ccy, 2)
fmt_str = "{:%s.%df}" % ("," if commas else "", max(0, prec))
return fmt_str.format(round(amount, prec))
try:
rounded_amount = round(amount, prec)
except decimal.InvalidOperation:
rounded_amount = amount
return fmt_str.format(rounded_amount)
def run(self):
# This runs from the plugins thread which catches exceptions

View File

@ -171,7 +171,10 @@ class Mnemonic(object):
n_custom = int(math.ceil(math.log(custom_entropy, 2)))
n = max(16, num_bits - n_custom)
print_error("make_seed", prefix, "adding %d bits"%n)
my_entropy = ecdsa.util.randrange(pow(2, n))
my_entropy = 1
while my_entropy < pow(2, n - bpw):
# try again if seed would not contain enough words
my_entropy = ecdsa.util.randrange(pow(2, n))
nonce = 0
while True:
nonce += 1

View File

@ -45,6 +45,14 @@ class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
class UnknownTxinType(Exception):
pass
class NotRecognizedRedeemScript(Exception):
pass
class BCDataStream(object):
def __init__(self):
self.input = None
@ -302,7 +310,8 @@ def parse_scriptSig(d, _bytes):
if match_decoded(decoded, match):
item = decoded[0][1]
if item[0] != 0:
# payto_pubkey
# assert item[0] == 0x30
# pay-to-pubkey
d['type'] = 'p2pk'
d['address'] = "(pubkey)"
d['signatures'] = [bh2u(item)]
@ -358,7 +367,7 @@ def parse_redeemScript(s):
match_multisig = [ op_m ] + [opcodes.OP_PUSHDATA4]*n + [ op_n, opcodes.OP_CHECKMULTISIG ]
if not match_decoded(dec2, match_multisig):
print_error("cannot find address in input script", bh2u(s))
return
raise NotRecognizedRedeemScript()
x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]]
pubkeys = [safe_parse_pubkey(x) for x in x_pubkeys]
redeemScript = multisig_script(pubkeys, m)

View File

@ -1,4 +1,4 @@
ELECTRUM_VERSION = '3.0.5' # version of the client package
ELECTRUM_VERSION = '3.0.6' # version of the client package
PROTOCOL_VERSION = '1.2' # protocol version requested
# The hash of the mnemonic seed must begin with this

View File

@ -360,7 +360,8 @@ class Abstract_Wallet(PrintError):
def add_unverified_tx(self, tx_hash, tx_height):
if tx_height == 0 and tx_hash in self.verified_tx:
self.verified_tx.pop(tx_hash)
self.verifier.merkle_roots.pop(tx_hash, None)
if self.verifier:
self.verifier.merkle_roots.pop(tx_hash, None)
# tx will be verified only if height > 0
if tx_hash not in self.verified_tx:

View File

@ -84,7 +84,8 @@ class WsClientThread(util.DaemonThread):
l = self.subscriptions.get(addr, [])
l.append((ws, amount))
self.subscriptions[addr] = l
self.network.send([('blockchain.address.subscribe', [addr])], self.response_queue.put)
h = self.network.addr_to_scripthash(addr)
self.network.send([('blockchain.scripthash.subscribe', [h])], self.response_queue.put)
def run(self):
@ -100,10 +101,13 @@ class WsClientThread(util.DaemonThread):
result = r.get('result')
if result is None:
continue
if method == 'blockchain.address.subscribe':
self.network.send([('blockchain.address.get_balance', params)], self.response_queue.put)
elif method == 'blockchain.address.get_balance':
addr = params[0]
if method == 'blockchain.scripthash.subscribe':
self.network.send([('blockchain.scripthash.get_balance', params)], self.response_queue.put)
elif method == 'blockchain.scripthash.get_balance':
h = params[0]
addr = self.network.h2addr.get(h, None)
if addr is None:
util.print_error("can't find address for scripthash: %s" % h)
l = self.subscriptions.get(addr, [])
for ws, amount in l:
if not ws.closed:

View File

@ -33,6 +33,10 @@ try:
except ImportError:
BTCHIP = False
MSG_NEEDS_FW_UPDATE_GENERIC = _('Firmware version too old. Please update at') + \
' https://www.ledgerwallet.com'
class Ledger_Client():
def __init__(self, hidDevice):
self.dongleObject = btchip(hidDevice)
@ -54,8 +58,23 @@ class Ledger_Client():
return ""
def i4b(self, x):
return pack('>I', x)
return pack('>I', x)
def test_pin_unlocked(func):
"""Function decorator to test the Ledger for being unlocked, and if not,
raise a human-readable exception.
"""
def catch_exception(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except BTChipException as e:
if e.sw == 0x6982:
raise Exception(_('Your Ledger is locked. Please unlock it.'))
else:
raise
return catch_exception
@test_pin_unlocked
def get_xpub(self, bip32_path, xtype):
self.checkDevice()
# bip32_path is of the form 44'/133'/1'
@ -72,7 +91,7 @@ class Ledger_Client():
if len(splitPath) > 1:
prevPath = "/".join(splitPath[0:len(splitPath) - 1])
nodeData = self.dongleObject.getWalletPublicKey(prevPath)
publicKey = compress_public_key(nodeData['publicKey'])#
publicKey = compress_public_key(nodeData['publicKey'])
h = hashlib.new('ripemd160')
h.update(hashlib.sha256(publicKey).digest())
fingerprint = unpack(">I", h.digest()[0:4])[0]
@ -117,7 +136,7 @@ class Ledger_Client():
if not checkFirmware(firmware):
self.dongleObject.dongle.close()
raise Exception("HW1 firmware version too old. Please update at https://www.ledgerwallet.com")
raise Exception(MSG_NEEDS_FW_UPDATE_GENERIC)
try:
self.dongleObject.getOperationMode()
except BTChipException as e:
@ -184,14 +203,14 @@ class Ledger_KeyStore(Hardware_KeyStore):
return obj
def get_derivation(self):
return self.derivation
return self.derivation
def get_client(self):
return self.plugin.get_client(self).dongleObject
def get_client_electrum(self):
return self.plugin.get_client(self)
def give_error(self, message, clear_client = False):
print_error(message)
if not self.signing:
@ -336,7 +355,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
self.handler.show_message(_("Confirm Transaction on your Ledger device..."))
try:
# Get trusted inputs from the original transactions
for utxo in inputs:
for utxo in inputs:
sequence = int_to_hex(utxo[5], 4)
if not p2shTransaction:
txtmp = bitcoinTransaction(bfh(utxo[0]))
@ -398,7 +417,7 @@ class LedgerPlugin(HW_PluginBase):
libraries_available = BTCHIP
keystore_class = Ledger_KeyStore
client = None
DEVICE_IDS = [
DEVICE_IDS = [
(0x2581, 0x1807), # HW.1 legacy btchip
(0x2581, 0x2b7c), # HW.1 transitional production
(0x2581, 0x3b7c), # HW.1 ledger production
@ -422,12 +441,12 @@ class LedgerPlugin(HW_PluginBase):
def get_btchip_device(self, device):
ledger = False
if (device.product_key[0] == 0x2581 and device.product_key[1] == 0x3b7c) or (device.product_key[0] == 0x2581 and device.product_key[1] == 0x4b7c) or (device.product_key[0] == 0x2c97):
ledger = True
ledger = True
dev = hid.device()
dev.open_path(device.path)
dev.set_nonblocking(True)
return HIDDongleHIDAPI(dev, ledger, BTCHIP_DEBUG)
def create_client(self, device, handler):
self.handler = handler
@ -436,7 +455,7 @@ class LedgerPlugin(HW_PluginBase):
client = Ledger_Client(client)
return client
def setup_device(self, device_info, wizard):
def setup_device(self, device_info, wizard):
devmgr = self.device_manager()
device_id = device_info.device.id_
client = devmgr.client_by_id(device_id)
@ -457,10 +476,10 @@ class LedgerPlugin(HW_PluginBase):
devmgr = self.device_manager()
handler = keystore.handler
with devmgr.hid_lock:
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
# returns the client for a given keystore. can use xpub
#if client:
# client.used()
if client is not None:
client.checkDevice()
client.checkDevice()
return client