electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit d304ccdf17be860b2828c1b6b31a6ba7aaa32020
parent 5be78950ca31ba903b4bd0f575511eeb732e114c
Author: Dmitry Sorokin <asfins@gmail.com>
Date:   Mon, 30 Jan 2017 12:36:56 +0300

py3 in qtgui

Diffstat:
Melectrum | 10+++++-----
Mgui/qt/address_list.py | 4++--
Mgui/qt/amountedit.py | 2+-
Mgui/qt/console.py | 7++-----
Mgui/qt/contact_list.py | 10+++++-----
Mgui/qt/history_list.py | 6+++---
Mgui/qt/installwizard.py | 6+++---
Mgui/qt/invoice_list.py | 6++++--
Mgui/qt/main_window.py | 36++++++++++++++++++------------------
Mgui/qt/network_dialog.py | 2+-
Mgui/qt/password_dialog.py | 6+++---
Mgui/qt/paytoedit.py | 12++++++------
Mgui/qt/qrtextedit.py | 6+++---
Mgui/qt/seed_dialog.py | 7++-----
Mgui/qt/util.py | 20++++++++++----------
Mgui/qt/utxo_list.py | 2+-
Mlib/bitcoin.py | 9+++++----
Mlib/commands.py | 8++++----
Mlib/daemon.py | 40+++++++++++++++++++++++-----------------
Mlib/exchange_rate.py | 2+-
Mlib/interface.py | 22++++++++++++++--------
Mlib/network.py | 33++++++++++++++++++++-------------
Mlib/paymentrequest.py | 12++++++------
Mlib/synchronizer.py | 8++++----
Mlib/transaction.py | 10+++++-----
Mlib/util.py | 125++++++++++++++++---------------------------------------------------------------
Mlib/wallet.py | 25+++++++++----------------
Mlib/x509.py | 151+++++++++++++++++++++++++++++++++++++------------------------------------------
28 files changed, 255 insertions(+), 332 deletions(-)

diff --git a/electrum b/electrum @@ -48,11 +48,11 @@ if jnius: threading.Thread.run = thread_check_run # monkeypatch unicode constructor for py3 -if six.PY3: - import builtins - builtins.unicode = str - builtins.QString = str - builtins.long = int +# if six.PY3: +# import builtins + # builtins.unicode = str + # builtins.QString = str + # builtins.long = int script_dir = os.path.dirname(os.path.realpath(__file__)) is_bundle = getattr(sys, 'frozen', False) diff --git a/gui/qt/address_list.py b/gui/qt/address_list.py @@ -48,7 +48,7 @@ class AddressList(MyTreeWidget): def on_update(self): self.wallet = self.parent.wallet item = self.currentItem() - current_address = item.data(0, Qt.UserRole).toString() if item else None + current_address = item.data(0, Qt.UserRole) if item else None self.clear() receiving_addresses = self.wallet.get_receiving_addresses() change_addresses = self.wallet.get_change_addresses() @@ -97,7 +97,7 @@ class AddressList(MyTreeWidget): can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 - addrs = [unicode(item.text(0)) for item in selected] + addrs = [item.text(0) for item in selected] if not addrs: return if not multi_select: diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py @@ -37,7 +37,7 @@ class AmountEdit(MyLineEdit): return 8 def numbify(self): - text = unicode(self.text()).strip() + text = self.text().strip() if text == '!': self.shortcut.emit() return diff --git a/gui/qt/console.py b/gui/qt/console.py @@ -76,7 +76,7 @@ class Console(QtGui.QPlainTextEdit): def getCommand(self): doc = self.document() - curr_line = unicode(doc.findBlockByLineNumber(doc.lineCount() - 1).text()) + curr_line = doc.findBlockByLineNumber(doc.lineCount() - 1).text() curr_line = curr_line.rstrip() curr_line = curr_line[len(self.prompt):] return curr_line @@ -86,7 +86,7 @@ class Console(QtGui.QPlainTextEdit): return doc = self.document() - curr_line = unicode(doc.findBlockByLineNumber(doc.lineCount() - 1).text()) + curr_line = doc.findBlockByLineNumber(doc.lineCount() - 1).text() self.moveCursor(QtGui.QTextCursor.End) for i in range(len(curr_line) - len(self.prompt)): self.moveCursor(QtGui.QTextCursor.Left, QtGui.QTextCursor.KeepAnchor) @@ -95,7 +95,6 @@ class Console(QtGui.QPlainTextEdit): self.textCursor().insertText(command) self.moveCursor(QtGui.QTextCursor.End) - def show_completions(self, completions): if self.completions_visible: self.hide_completions() @@ -113,7 +112,6 @@ class Console(QtGui.QPlainTextEdit): self.moveCursor(QtGui.QTextCursor.End) self.completions_visible = True - def hide_completions(self): if not self.completions_visible: return @@ -125,7 +123,6 @@ class Console(QtGui.QPlainTextEdit): self.moveCursor(QtGui.QTextCursor.End) self.completions_visible = False - def getConstruct(self, command): if self.construct: prev_command = self.construct[-1] diff --git a/gui/qt/contact_list.py b/gui/qt/contact_list.py @@ -55,7 +55,7 @@ class ContactList(MyTreeWidget): def on_edited(self, item, column, prior): if column == 0: # Remove old contact if renamed self.parent.contacts.pop(prior) - self.parent.set_contact(unicode(item.text(0)), unicode(item.text(1))) + self.parent.set_contact(item.text(0), item.text(1)) def import_contacts(self): wallet_folder = self.parent.get_wallet_folder() @@ -72,11 +72,11 @@ class ContactList(MyTreeWidget): menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog()) menu.addAction(_("Import file"), lambda: self.parent.import_contacts()) else: - names = [unicode(item.text(0)) for item in selected] - keys = [unicode(item.text(1)) for item in selected] + names = [item.text(0) for item in selected] + keys = [item.text(1) for item in selected] column = self.currentColumn() column_title = self.headerItem().text(column) - column_data = '\n'.join([unicode(item.text(column)) for item in selected]) + column_data = '\n'.join([item.text(column) for item in selected]) menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, column)) @@ -91,7 +91,7 @@ class ContactList(MyTreeWidget): def on_update(self): item = self.currentItem() - current_key = item.data(0, Qt.UserRole).toString() if item else None + current_key = item.data(0, Qt.UserRole) if item else None self.clear() for key in sorted(self.parent.contacts.keys()): _type, name = self.parent.contacts[key] diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py @@ -77,7 +77,7 @@ class HistoryList(MyTreeWidget): self.wallet = self.parent.wallet h = self.wallet.get_history(self.get_domain()) item = self.currentItem() - current_tx = item.data(0, Qt.UserRole).toString() if item else None + current_tx = item.data(0, Qt.UserRole) if item else None self.clear() fx = self.parent.fx if fx: fx.history_used_spot = False @@ -128,7 +128,7 @@ class HistoryList(MyTreeWidget): child_count = root.childCount() for i in range(child_count): item = root.child(i) - txid = str(item.data(0, Qt.UserRole).toString()) + txid = item.data(0, Qt.UserRole) label = self.wallet.get_label(txid) item.setText(3, label) @@ -147,7 +147,7 @@ class HistoryList(MyTreeWidget): if not item: return column = self.currentColumn() - tx_hash = str(item.data(0, Qt.UserRole).toString()) + tx_hash = item.data(0, Qt.UserRole) if not tx_hash: return if column is 0: diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -444,8 +444,8 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): @wizard_dialog def choice_dialog(self, title, message, choices, run_next): - c_values = map(lambda x: x[0], choices) - c_titles = map(lambda x: x[1], choices) + c_values = [x[0] for x in choices] + c_titles = [x[1] for x in choices] clayout = ChoicesLayout(message, c_titles) vbox = QVBoxLayout() vbox.addLayout(clayout.layout()) @@ -473,7 +473,7 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): vbox.addWidget(line) vbox.addWidget(WWLabel(warning)) self.exec_layout(vbox, title, next_enabled=test(default)) - return ' '.join(unicode(line.text()).split()) + return ' '.join(line.text().split()) @wizard_dialog def show_xpub_dialog(self, xpub, run_next): diff --git a/gui/qt/invoice_list.py b/gui/qt/invoice_list.py @@ -73,8 +73,10 @@ class InvoiceList(MyTreeWidget): def create_menu(self, position): menu = QMenu() item = self.itemAt(position) - key = str(item.data(0, 32).toString()) - column = self.currentColumn() + if not item: + return + key = item.data(0, 32) + column = self.currentColumn() column_title = self.headerItem().text(column) column_data = item.text(column) pr = self.parent.invoices.get(key) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -384,7 +384,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def open_wallet(self): wallet_folder = self.get_wallet_folder() - filename = unicode(QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)) + filename = QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) if not filename: return self.gui_object.new_window(filename) @@ -393,7 +393,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def backup_wallet(self): path = self.wallet.storage.path wallet_folder = os.path.dirname(path) - filename = unicode( QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder) ) + filename = QFileDialog.getSaveFileName(self, _('Enter a filename for the copy of your wallet'), wallet_folder) if not filename: return @@ -578,16 +578,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # custom wrappers for getOpenFileName and getSaveFileName, that remember the path selected by the user def getOpenFileName(self, title, filter = ""): - directory = self.config.get('io_dir', unicode(os.path.expanduser('~'))) - fileName = unicode( QFileDialog.getOpenFileName(self, title, directory, filter) ) + directory = self.config.get('io_dir', os.path.expanduser('~')) + fileName = QFileDialog.getOpenFileName(self, title, directory, filter) if fileName and directory != os.path.dirname(fileName): self.config.set_key('io_dir', os.path.dirname(fileName), True) return fileName def getSaveFileName(self, title, filename, filter = ""): - directory = self.config.get('io_dir', unicode(os.path.expanduser('~'))) + directory = self.config.get('io_dir', os.path.expanduser('~')) path = os.path.join( directory, filename ) - fileName = unicode( QFileDialog.getSaveFileName(self, title, path, filter) ) + fileName = QFileDialog.getSaveFileName(self, title, path, filter) if fileName and directory != os.path.dirname(fileName): self.config.set_key('io_dir', os.path.dirname(fileName), True) return fileName @@ -880,7 +880,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def save_payment_request(self): addr = str(self.receive_address_e.text()) amount = self.receive_amount_e.get_amount() - message = unicode(self.receive_message_e.text()) + message = self.receive_message_e.text() if not message and not amount: self.show_error(_('No message or amount')) return False @@ -982,7 +982,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def update_receive_qr(self): addr = str(self.receive_address_e.text()) amount = self.receive_amount_e.get_amount() - message = unicode(self.receive_message_e.text()).encode('utf8') + message = self.receive_message_e.text() self.save_request_button.setEnabled((amount is not None) or (message != "")) uri = util.create_URI(addr, amount, message) self.receive_qr.setData(uri) @@ -1281,7 +1281,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if self.payment_request and self.payment_request.has_expired(): self.show_error(_('Payment request has expired')) return - label = unicode( self.message_e.text() ) + label = self.message_e.text() if self.payment_request: outputs = self.payment_request.get_outputs() @@ -1513,7 +1513,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if not URI: return try: - out = util.parse_URI(unicode(URI), self.on_pr) + out = util.parse_URI(URI, self.on_pr) except BaseException as e: self.show_error(_('Invalid bitcoin URI:') + '\n' + str(e)) return @@ -1807,7 +1807,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): vbox.addLayout(grid) vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) if d.exec_(): - self.set_contact(unicode(line2.text()), str(line1.text())) + self.set_contact(line2.text(), line1.text()) def show_master_public_keys(self): dialog = WindowModalDialog(self, "Master Public Keys") @@ -1848,7 +1848,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): except BaseException as e: self.show_error(str(e)) return - from seed_dialog import SeedDialog + from .seed_dialog import SeedDialog d = SeedDialog(self, seed, passphrase) d.exec_() @@ -1889,7 +1889,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): @protected def do_sign(self, address, message, signature, password): address = str(address.text()).strip() - message = unicode(message.toPlainText()).encode('utf-8').strip() + message = message.toPlainText().strip() if not bitcoin.is_address(address): self.show_message('Invalid Bitcoin address.') return @@ -1906,7 +1906,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def do_verify(self, address, message, signature): address = str(address.text()).strip() - message = unicode(message.toPlainText()).encode('utf-8').strip() + message = message.toPlainText().strip() if not bitcoin.is_address(address): self.show_message('Invalid Bitcoin address.') return @@ -1970,10 +1970,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.wallet.thread.add(task, on_success=message_e.setText) def do_encrypt(self, message_e, pubkey_e, encrypted_e): - message = unicode(message_e.toPlainText()) + message = message_e.toPlainText() message = message.encode('utf-8') try: - encrypted = bitcoin.encrypt_message(message, str(pubkey_e.text())) + encrypted = bitcoin.encrypt_message(message, pubkey_e.text()) encrypted_e.setText(encrypted) except BaseException as e: traceback.print_exc(file=sys.stdout) @@ -2386,7 +2386,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): lang_label = HelpLabel(_('Language') + ':', lang_help) lang_combo = QComboBox() from electrum.i18n import languages - lang_combo.addItems(languages.values()) + lang_combo.addItems(list(languages.values())) try: index = languages.keys().index(self.config.get("language",'')) except Exception: @@ -2570,7 +2570,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): msg = _("Install the zbar package to enable this.") qr_label = HelpLabel(_('Video Device') + ':', msg) qr_combo.setEnabled(qrscanner.libzbar is not None) - on_video_device = lambda x: self.config.set_key("video_device", str(qr_combo.itemData(x).toString()), True) + on_video_device = lambda x: self.config.set_key("video_device", qr_combo.itemData(x), True) qr_combo.currentIndexChanged.connect(on_video_device) gui_widgets.append((qr_label, qr_combo)) diff --git a/gui/qt/network_dialog.py b/gui/qt/network_dialog.py @@ -386,7 +386,7 @@ class NetworkChoiceLayout(object): def change_protocol(self, use_ssl): p = 's' if use_ssl else 't' - host = unicode(self.server_host.text()) + host = self.server_host.text() pp = self.servers.get(host, DEFAULT_PORTS) if p not in pp.keys(): p = pp.keys()[0] diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py @@ -44,7 +44,7 @@ def check_password_strength(password): :param password: password entered by user in New Password :return: password strength Weak or Medium or Strong ''' - password = unicode(password) + password = password n = math.log(len(set(password))) num = re.search("[0-9]", password) is not None and re.match("^[0-9]*$", password) is None caps = password != password.upper() and password != password.lower() @@ -156,11 +156,11 @@ class PasswordLayout(object): def old_password(self): if self.kind == PW_CHANGE: - return unicode(self.pw.text()) or None + return self.pw.text() or None return None def new_password(self): - pw = unicode(self.new_pw.text()) + pw = self.new_pw.text() # Empty passphrases are fine and returned empty. if pw == "" and self.kind != PW_PASSPHRASE: pw = None diff --git a/gui/qt/paytoedit.py b/gui/qt/paytoedit.py @@ -120,7 +120,7 @@ class PayToEdit(ScanQRTextEdit): if self.is_pr: return # filter out empty lines - lines = filter(lambda x: x, self.lines()) + lines = [i for i in self.lines() if i] outputs = [] total = 0 self.payto_address = None @@ -180,7 +180,7 @@ class PayToEdit(ScanQRTextEdit): return self.outputs[:] def lines(self): - return unicode(self.toPlainText()).split('\n') + return self.toPlainText().split('\n') def is_multiline(self): return len(self.lines()) > 1 @@ -242,14 +242,14 @@ class PayToEdit(ScanQRTextEdit): QPlainTextEdit.keyPressEvent(self, e) ctrlOrShift = e.modifiers() and (Qt.ControlModifier or Qt.ShiftModifier) - if self.c is None or (ctrlOrShift and e.text().isEmpty()): + if self.c is None or (ctrlOrShift and not e.text()): return - eow = QString("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=") - hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift; + eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" + hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift completionPrefix = self.textUnderCursor() - if hasModifier or e.text().isEmpty() or completionPrefix.length() < 1 or eow.contains(e.text().right(1)): + if hasModifier or not e.text() or completionPrefix.length() < 1 or eow.contains(e.text().right(1)): self.c.popup().hide() return diff --git a/gui/qt/qrtextedit.py b/gui/qt/qrtextedit.py @@ -22,11 +22,11 @@ class ShowQRTextEdit(ButtonsTextEdit): run_hook('show_text_edit', self) def qr_show(self): - from qrcodewidget import QRDialog + from .qrcodewidget import QRDialog try: s = str(self.toPlainText()) except: - s = unicode(self.toPlainText()) + s = self.toPlainText() QRDialog(s).exec_() def contextMenuEvent(self, e): @@ -45,7 +45,7 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin): run_hook('scan_text_edit', self) def file_input(self): - fileName = unicode(QFileDialog.getOpenFileName(self, 'select file')) + fileName = QFileDialog.getOpenFileName(self, 'select file') if not fileName: return with open(fileName, "r") as f: diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py @@ -52,7 +52,6 @@ def seed_warning_msg(seed): ]) % len(seed.split()) - class SeedLayout(QVBoxLayout): #options is_bip39 = False @@ -92,7 +91,6 @@ class SeedLayout(QVBoxLayout): self.is_ext = cb_ext.isChecked() if 'ext' in self.options else False self.is_bip39 = cb_bip39.isChecked() if 'bip39' in self.options else False - def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None): QVBoxLayout.__init__(self) self.parent = parent @@ -140,7 +138,7 @@ class SeedLayout(QVBoxLayout): self.addWidget(self.seed_warning) def get_seed(self): - text = unicode(self.seed_e.text()) + text = self.seed_e.text() return ' '.join(text.split()) def on_edit(self): @@ -159,7 +157,6 @@ class SeedLayout(QVBoxLayout): self.parent.next_button.setEnabled(b) - class KeysLayout(QVBoxLayout): def __init__(self, parent=None, title=None, is_valid=None): QVBoxLayout.__init__(self) @@ -171,7 +168,7 @@ class KeysLayout(QVBoxLayout): self.addWidget(self.text_e) def get_text(self): - return unicode(self.text_e.text()) + return self.text_e.text() def on_edit(self): b = self.is_valid(self.get_text()) diff --git a/gui/qt/util.py b/gui/qt/util.py @@ -260,7 +260,7 @@ def line_dialog(parent, title, label, ok_label, default=None): l.addWidget(txt) l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label))) if dialog.exec_(): - return unicode(txt.text()) + return txt.text() def text_dialog(parent, title, label, ok_label, default=None): from qrtextedit import ScanQRTextEdit @@ -275,7 +275,7 @@ def text_dialog(parent, title, label, ok_label, default=None): l.addWidget(txt) l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label))) if dialog.exec_(): - return unicode(txt.toPlainText()) + return txt.toPlainText() class ChoicesLayout(object): def __init__(self, msg, choices, on_clicked=None, checked_index=0): @@ -341,15 +341,15 @@ def filename_field(parent, config, defaultname, select_msg): hbox = QHBoxLayout() - directory = config.get('io_dir', unicode(os.path.expanduser('~'))) + directory = config.get('io_dir', os.path.expanduser('~')) path = os.path.join( directory, defaultname ) filename_e = QLineEdit() filename_e.setText(path) def func(): - text = unicode(filename_e.text()) + text = filename_e.text() _filter = "*.csv" if text.endswith(".csv") else "*.json" if text.endswith(".json") else None - p = unicode( QFileDialog.getSaveFileName(None, select_msg, text, _filter)) + p = QFileDialog.getSaveFileName(None, select_msg, text, _filter) if p: filename_e.setText(p) @@ -360,7 +360,7 @@ def filename_field(parent, config, defaultname, select_msg): vbox.addLayout(hbox) def set_csv(v): - text = unicode(filename_e.text()) + text = filename_e.text() text = text.replace(".json",".csv") if v else text.replace(".csv",".json") filename_e.setText(text) @@ -409,7 +409,7 @@ class MyTreeWidget(QTreeWidget): def editItem(self, item, column): if column in self.editable_columns: - self.editing_itemcol = (item, column, unicode(item.text(column))) + self.editing_itemcol = (item, column, item.text(column)) # Calling setFlags causes on_changed events for some reason item.setFlags(item.flags() | Qt.ItemIsEditable) QTreeWidget.editItem(self, item, column) @@ -471,7 +471,7 @@ class MyTreeWidget(QTreeWidget): def on_edited(self, item, column, prior): '''Called only when the text actually changes''' key = str(item.data(0, Qt.UserRole).toString()) - text = unicode(item.text(column)) + text = item.text(column) self.parent.wallet.set_label(key, text) self.parent.history_list.update_labels() self.parent.update_completions() @@ -501,10 +501,10 @@ class MyTreeWidget(QTreeWidget): def filter(self, p): columns = self.__class__.filter_columns - p = unicode(p).lower() + p = p.lower() self.current_filter = p for item in self.get_leaves(self.invisibleRootItem()): - item.setHidden(all([unicode(item.text(column)).lower().find(p) == -1 + item.setHidden(all([item.text(column).lower().find(p) == -1 for column in columns])) diff --git a/gui/qt/utxo_list.py b/gui/qt/utxo_list.py @@ -63,7 +63,7 @@ class UTXOList(MyTreeWidget): self.addChild(utxo_item) def create_menu(self, position): - selected = [str(x.data(0, Qt.UserRole).toString()) for x in self.selectedItems()] + selected = [x.data(0, Qt.UserRole) for x in self.selectedItems()] if not selected: return menu = QMenu() diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -28,10 +28,11 @@ import hashlib import base64 import re import hmac +import os from lib.util import bfh, bh2u from . import version -from .util import print_error, InvalidPassword, assert_bytes, _bytes, to_bytes +from .util import print_error, InvalidPassword, assert_bytes, to_bytes import ecdsa import pyaes @@ -206,12 +207,12 @@ def op_push(i): def sha256(x): x = to_bytes(x, 'utf8') - return _bytes(hashlib.sha256(x).digest()) + return bytes(hashlib.sha256(x).digest()) def Hash(x): x = to_bytes(x, 'utf8') - out = _bytes(sha256(sha256(x))) + out = bytes(sha256(sha256(x))) return out @@ -363,7 +364,7 @@ def base_decode(v, length, base): chars = __b43chars long_value = 0 for (i, c) in enumerate(v[::-1]): - long_value += chars.find(_bytes([c])) * (base**i) + long_value += chars.find(bytes([c])) * (base**i) result = bytearray() while long_value >= 256: div, mod = divmod(long_value, 256) diff --git a/lib/commands.py b/lib/commands.py @@ -243,8 +243,8 @@ class Commands: tx = Transaction(tx) if privkey: pubkey = bitcoin.public_key_from_private_key(privkey) - h160 = bitcoin.hash_160(pubkey.decode('hex')) - x_pubkey = 'fd' + (chr(0) + h160).encode('hex') + h160 = bitcoin.hash_160(bfh(pubkey)) + x_pubkey = 'fd' + bh2u(b'\x00' + h160) tx.sign({x_pubkey:privkey}) else: self.wallet.sign_transaction(tx, password) @@ -266,8 +266,8 @@ class Commands: def createmultisig(self, num, pubkeys): """Create multisig address""" assert isinstance(pubkeys, list), (type(num), type(pubkeys)) - redeem_script = transaction.multisig_script(pubkeys, num) - address = bitcoin.hash160_to_p2sh(hash_160(redeem_script.decode('hex'))) + redeem_script = Transaction.multisig_script(pubkeys, num) + address = bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) return {'address':address, 'redeemScript':redeem_script} @command('w') diff --git a/lib/daemon.py b/lib/daemon.py @@ -33,8 +33,9 @@ import os import sys import time -# import jsonrpclib -# from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler +# from jsonrpc import JSONRPCResponseManager +import jsonrpclib +from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler from .version import ELECTRUM_VERSION from .network import Network @@ -47,12 +48,15 @@ from .simple_config import SimpleConfig from .plugins import run_hook from .exchange_rate import FxThread + def get_lockfile(config): return os.path.join(config.path, 'daemon') + def remove_lockfile(lockfile): os.unlink(lockfile) + def get_fd_or_server(config): '''Tries to create the lockfile, using O_EXCL to prevent races. If it succeeds it returns the FD. @@ -71,6 +75,7 @@ def get_fd_or_server(config): # Couldn't connect; remove lockfile and try again. remove_lockfile(lockfile) + def get_server(config): lockfile = get_lockfile(config) while True: @@ -82,7 +87,8 @@ def get_server(config): # Test daemon is running server.ping() return server - except: + except Exception as e: + print_error(e) pass if not create_time or create_time < time.time() - 1.0: return None @@ -90,17 +96,17 @@ def get_server(config): time.sleep(1.0) -# class RequestHandler(SimpleJSONRPCRequestHandler): -# -# def do_OPTIONS(self): -# self.send_response(200) -# self.end_headers() -# -# def end_headers(self): -# self.send_header("Access-Control-Allow-Headers", -# "Origin, X-Requested-With, Content-Type, Accept") -# self.send_header("Access-Control-Allow-Origin", "*") -# SimpleJSONRPCRequestHandler.end_headers(self) +class RequestHandler(SimpleJSONRPCRequestHandler): + + def do_OPTIONS(self): + self.send_response(200) + self.end_headers() + + def end_headers(self): + self.send_header("Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept") + self.send_header("Access-Control-Allow-Origin", "*") + SimpleJSONRPCRequestHandler.end_headers(self) class Daemon(DaemonThread): @@ -129,12 +135,12 @@ class Daemon(DaemonThread): try: server = SimpleJSONRPCServer((host, port), logRequests=False, requestHandler=RequestHandler) - except: - self.print_error('Warning: cannot initialize RPC server on host', host) + except Exception as e: + self.print_error('Warning: cannot initialize RPC server on host', host, e) self.server = None os.close(fd) return - os.write(fd, repr((server.socket.getsockname(), time.time()))) + os.write(fd, bytes(repr((server.socket.getsockname(), time.time())), 'utf8')) os.close(fd) server.timeout = 0.1 for cmdname in known_commands: diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py @@ -290,7 +290,7 @@ class LocalBitcoins(ExchangeBase): class MercadoBitcoin(ExchangeBase): def get_rates(self, ccy): - json = self.get_json('api.bitvalor.com', '/v1/ticker.json') + json = self.get_json('api.bitvalor.com', '/v1/ticker.json') return {'BRL': Decimal(json['ticker_1h']['exchanges']['MBT']['last'])} diff --git a/lib/interface.py b/lib/interface.py @@ -38,6 +38,9 @@ import time import traceback import requests + +from lib import print_error + ca_path = requests.certs.where() from . import util @@ -60,6 +63,7 @@ def Connection(server, queue, config_path): c.start() return c + class TcpConnection(threading.Thread, util.PrintError): def __init__(self, server, queue, config_path): @@ -131,8 +135,9 @@ class TcpConnection(threading.Thread, util.PrintError): return # try with CA first try: - s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv23, cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_path, do_handshake_on_connect=True) + s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1_1, cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_path, do_handshake_on_connect=True) except ssl.SSLError as e: + print_error(e) s = None if s and self.check_host_name(s.getpeercert(), self.host): self.print_error("SSL certificate signed by CA") @@ -143,7 +148,7 @@ class TcpConnection(threading.Thread, util.PrintError): if s is None: return try: - s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv23, cert_reqs=ssl.CERT_NONE, ca_certs=None) + s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1_1, cert_reqs=ssl.CERT_NONE, ca_certs=None) except ssl.SSLError as e: self.print_error("SSL error retrieving SSL certificate:", e) return @@ -166,9 +171,9 @@ class TcpConnection(threading.Thread, util.PrintError): if self.use_ssl: try: s = ssl.wrap_socket(s, - ssl_version=ssl.PROTOCOL_SSLv23, + ssl_version=ssl.PROTOCOL_TLSv1_1, cert_reqs=ssl.CERT_REQUIRED, - ca_certs= (temporary_path if is_new else cert_path), + ca_certs=(temporary_path if is_new else cert_path), do_handshake_on_connect=True) except ssl.SSLError as e: self.print_error("SSL error:", e) @@ -196,11 +201,11 @@ class TcpConnection(threading.Thread, util.PrintError): os.unlink(cert_path) return self.print_error("wrong certificate") + if e.errno == 104: + return return except BaseException as e: self.print_error(e) - if e.errno == 104: - return traceback.print_exc(file=sys.stderr) return @@ -216,6 +221,7 @@ class TcpConnection(threading.Thread, util.PrintError): self.print_error("connected") self.queue.put((self.server, socket)) + class Interface(util.PrintError): """The Interface class handles a socket connected to a single remote electrum server. It's exposed API is: @@ -274,7 +280,7 @@ class Interface(util.PrintError): n = self.num_requests() wire_requests = self.unsent_requests[0:n] try: - self.pipe.send_all(map(make_dict, wire_requests)) + self.pipe.send_all([make_dict(*r) for r in wire_requests]) except socket.error as e: self.print_error("socket error:", e) return False @@ -368,13 +374,13 @@ def _match_hostname(name, val): return val.startswith('*.') and name.endswith(val[1:]) + def test_certificates(): from .simple_config import SimpleConfig config = SimpleConfig() mydir = os.path.join(config.path, "certs") certs = os.listdir(mydir) for c in certs: - print(c) p = os.path.join(mydir,c) with open(p) as f: cert = f.read() diff --git a/lib/network.py b/lib/network.py @@ -103,7 +103,7 @@ SERVER_RETRY_INTERVAL = 10 def parse_servers(result): """ parse servers list into dict format""" - from version import PROTOCOL_VERSION + from .version import PROTOCOL_VERSION servers = {} for item in result: host = item[1] @@ -123,7 +123,8 @@ def parse_servers(result): if pruning_level == '': pruning_level = '0' try: is_recent = cmp(util.normalize_version(version), util.normalize_version(PROTOCOL_VERSION)) >= 0 - except Exception: + except Exception as e: + print_error(e) is_recent = False if out and is_recent: @@ -152,13 +153,15 @@ from .simple_config import SimpleConfig proxy_modes = ['socks4', 'socks5', 'http'] + def serialize_proxy(p): - if type(p) != dict: + if not isinstance(p, dict): return None return ':'.join([p.get('mode'),p.get('host'), p.get('port'), p.get('user'), p.get('password')]) + def deserialize_proxy(s): - if type(s) not in [str, unicode]: + if not isinstance(s, str): return None if s.lower() == 'none': return None @@ -183,15 +186,18 @@ def deserialize_proxy(s): proxy["password"] = args[n] return proxy + def deserialize_server(server_str): host, port, protocol = str(server_str).split(':') assert protocol in 'st' int(port) # Throw if cannot be converted to int return host, port, protocol + def serialize_server(host, port, protocol): return str(':'.join([host, port, protocol])) + class Network(util.DaemonThread): """The Network class manages a set of connections to remote electrum servers, each connected socket is handled by an Interface() object. @@ -209,7 +215,7 @@ class Network(util.DaemonThread): if config is None: config = {} # Do not use mutables as default values! util.DaemonThread.__init__(self) - self.config = SimpleConfig(config) if type(config) == type({}) else config + self.config = SimpleConfig(config) if isinstance(config, dict) else config self.num_server = 10 if not self.config.get('oneserver') else 0 self.blockchains = blockchain.read_blockchains(self.config) self.print_error("blockchains", self.blockchains.keys()) @@ -390,7 +396,7 @@ class Network(util.DaemonThread): def get_interfaces(self): '''The interfaces that are in connected state''' - return self.interfaces.keys() + return list(self.interfaces.keys()) def get_servers(self): if self.irc_servers: @@ -456,7 +462,7 @@ class Network(util.DaemonThread): def stop_network(self): self.print_error("stopping network") - for interface in self.interfaces.values(): + for interface in list(self.interfaces.values()): self.close_interface(interface) if self.interface: self.close_interface(self.interface) @@ -596,7 +602,7 @@ class Network(util.DaemonThread): def get_index(self, method, params): """ hashable index for subscriptions and cache""" - return str(method) + (':' + str(params[0]) if params else '') + return str(method) + (':' + str(params[0]) if params else '') def process_responses(self, interface): responses = interface.get_responses() @@ -647,6 +653,7 @@ class Network(util.DaemonThread): def send(self, messages, callback): '''Messages is a list of (method, params) tuples''' + messages = list(messages) with self.lock: self.pending_sends.append((messages, callback)) @@ -730,7 +737,8 @@ class Network(util.DaemonThread): self.connection_down(server) # Send pings and shut down stale interfaces - for interface in self.interfaces.values(): + # must use copy of values + for interface in list(self.interfaces.values()): if interface.has_timed_out(): self.connection_down(interface.server) elif interface.ping_required(): @@ -1059,15 +1067,14 @@ class Network(util.DaemonThread): host, port, protocol = server.split(':') self.set_parameters(host, port, protocol, proxy, auto_connect) - def get_local_height(self): return self.blockchain().height() def synchronous_get(self, request, timeout=30): - queue = queue.Queue() - self.send([request], queue.put) + q = queue.Queue() + self.send([request], q.put) try: - r = queue.get(True, timeout) + r = q.get(True, timeout) except queue.Empty: raise BaseException('Server did not answer') if r.get('error'): diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py @@ -51,7 +51,7 @@ except ImportError: from . import bitcoin from . import util -from .util import print_error +from .util import print_error, bh2u, bfh from . import transaction from . import x509 from . import rsakey @@ -126,7 +126,7 @@ class PaymentRequest: def parse(self, r): if self.error: return - self.id = bitcoin.sha256(r)[0:16].encode('hex') + self.id = bh2u(bitcoin.sha256(r)[0:16]) try: self.data = pb2.PaymentRequest() self.data.ParseFromString(r) @@ -321,7 +321,7 @@ def make_unsigned_request(req): if amount is None: amount = 0 memo = req['memo'] - script = Transaction.pay_script(TYPE_ADDRESS, addr).decode('hex') + script = bfh(Transaction.pay_script(TYPE_ADDRESS, addr)) outputs = [(script, amount)] pd = pb2.PaymentDetails() for script, amount in outputs: @@ -445,7 +445,7 @@ def serialize_request(req): signature = req.get('sig') requestor = req.get('name') if requestor and signature: - pr.signature = signature.decode('hex') + pr.signature = bfh(signature) pr.pki_type = 'dnssec+btc' pr.pki_data = str(requestor) return pr @@ -477,7 +477,7 @@ class InvoiceStore(object): def load(self, d): for k, v in d.items(): try: - pr = PaymentRequest(v.get('hex').decode('hex')) + pr = bfh(PaymentRequest(v.get('hex'))) pr.tx = v.get('txid') pr.requestor = v.get('requestor') self.invoices[k] = pr @@ -499,7 +499,7 @@ class InvoiceStore(object): l = {} for k, pr in self.invoices.items(): l[k] = { - 'hex': str(pr).encode('hex'), + 'hex': bh2u(pr), 'requestor': pr.requestor, 'txid': pr.tx } diff --git a/lib/synchronizer.py b/lib/synchronizer.py @@ -33,7 +33,7 @@ import hashlib from .bitcoin import Hash, hash_encode from .transaction import Transaction -from .util import print_error, print_msg, ThreadJob +from .util import print_error, print_msg, ThreadJob, bh2u class Synchronizer(ThreadJob): @@ -89,7 +89,7 @@ class Synchronizer(ThreadJob): status = '' for tx_hash, height in h: status += tx_hash + ':%d:' % height - return hashlib.sha256(status).digest().encode('hex') + return bh2u(hashlib.sha256(status.encode('ascii')).digest()) def addr_subscription_response(self, response): params, result = self.parse_response(response) @@ -114,7 +114,7 @@ class Synchronizer(ThreadJob): self.print_error("receiving history", addr, len(result)) server_status = self.requested_histories[addr] hashes = set(map(lambda item: item['tx_hash'], result)) - hist = map(lambda item: (item['tx_hash'], item['height']), result) + hist = list(map(lambda item: (item['tx_hash'], item['height']), result)) # tx_fees tx_fees = [(item['tx_hash'], item.get('fee')) for item in result] tx_fees = dict(filter(lambda x:x[1] is not None, tx_fees)) @@ -140,7 +140,7 @@ class Synchronizer(ThreadJob): if not params: return tx_hash, tx_height = params - #assert tx_hash == hash_encode(Hash(result.decode('hex'))) + #assert tx_hash == hash_encode(Hash(bytes.fromhex(result))) tx = Transaction(result) try: tx.deserialize() diff --git a/lib/transaction.py b/lib/transaction.py @@ -170,9 +170,9 @@ class Enumeration: for x in enumList: if isinstance(x, tuple): x, i = x - if not isinstance(x, six.text_type): + if not isinstance(x, str): raise EnumException("enum name is not a string: " + x) - if not isinstance(i, six.integer_types): + if not isinstance(i, int): raise EnumException("enum value is not an integer: " + i) if x in uniqueNames: raise EnumException("enum name is not unique: " + x) @@ -715,9 +715,9 @@ class Transaction: txin = inputs[i] # TODO: py3 hex if self.is_segwit_input(txin): - hashPrevouts = Hash(''.join(self.serialize_outpoint(txin) for txin in inputs).decode('hex')).encode('hex') - hashSequence = Hash(''.join(int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) for txin in inputs).decode('hex')).encode('hex') - hashOutputs = Hash(''.join(self.serialize_output(o) for o in outputs).decode('hex')).encode('hex') + hashPrevouts = bh2u(Hash(bfh(''.join(self.serialize_outpoint(txin) for txin in inputs)))) + hashSequence = bh2u(Hash(bfh(''.join(int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) for txin in inputs)))) + hashOutputs = bh2u(Hash(bfh(''.join(self.serialize_output(o) for o in outputs)))) outpoint = self.serialize_outpoint(txin) preimage_script = self.get_preimage_script(txin) scriptCode = var_int(len(preimage_script)/2) + preimage_script diff --git a/lib/util.py b/lib/util.py @@ -141,7 +141,7 @@ class DaemonThread(threading.Thread, PrintError): for job in self.jobs: try: job.run() - except: + except Exception as e: traceback.print_exc(file=sys.stderr) def remove_jobs(self, jobs): @@ -170,7 +170,8 @@ class DaemonThread(threading.Thread, PrintError): self.print_error("stopped") -is_verbose = False +# TODO: disable +is_verbose = True def set_verbosity(b): global is_verbose is_verbose = b @@ -281,63 +282,8 @@ def assert_str(*args): assert isinstance(x, six.string_types) -def __str(x, encoding='utf8'): - if six.PY3: - return x.decode(encoding) - - -def _bytes(x=None, encoding=None, **kw): - """ - py2-py3 aware wrapper to "bytes()" like constructor - :param x: - :return: - """ - if encoding is not None: - kw['encoding'] = encoding - if x is None: - x = [] - if six.PY3: - if isinstance(x, bytes): - return x - return bytes(x, **kw) - else: - return bytearray(x, **kw) - - -def _to_bytes2(x, enc): - if isinstance(x, bytearray): - return bytearray(x) - if isinstance(x, six.text_type): - return bytearray(x.encode(enc)) - elif isinstance(x, six.binary_type): - return bytearray(x) - else: - raise TypeError("Not a string or bytes like object") - -def _to_bytes3(x, enc): - if isinstance(x, bytes): - return x - if isinstance(x, str): - return x.encode(enc) - elif isinstance(x, bytearray): - return bytes(x) - else: - raise TypeError("Not a string or bytes like object") - - -def _to_string2(x, enc): - if isinstance(x, (str, bytes)): - return x - if isinstance(x, unicode): - return x.encode(enc) - if isinstance(x, bytearray): - return x.decode(enc) - else: - raise TypeError("Not a string or bytes like object") - - -def _to_string3(x, enc): +def to_string(x, enc): if isinstance(x, (bytes, bytearray)): return x.decode(enc) if isinstance(x, str): @@ -349,35 +295,16 @@ def to_bytes(something, encoding='utf8'): """ cast string to bytes() like object, but for python2 support it's bytearray copy """ - raise NotImplementedError("This call should be redefined") - -def to_bytes(something, encoding='utf8'): - """ - cast string to str object - """ - raise NotImplementedError("This call should be redefined") - -if six.PY3: - to_bytes = _to_bytes3 - to_string = _to_string3 -else: - to_bytes = _to_bytes2 - to_string = _to_string2 - -if six.PY3: - bfh_builder = lambda x: bytes.fromhex(x) -else: - bfh_builder = lambda x: x.decode('hex') # str(bytearray.fromhex(x)) - + if isinstance(something, bytes): + return something + if isinstance(something, str): + return something.encode(encoding) + elif isinstance(something, bytearray): + return bytes(something) + else: + raise TypeError("Not a string or bytes like object") -# def ufh(x): -# """ -# py2-py3 aware wrapper for str.decode('hex') -# :param x: str -# :return: str -# """ -# if -# return binascii.unhexlify(x) +bfh_builder = lambda x: bytes.fromhex(x) def hfu(x): @@ -700,20 +627,18 @@ else: builtins.input = raw_input - def parse_json(message): - n = message.find('\n') + # TODO: check \r\n pattern + n = message.find(b'\n') if n==-1: return None, message try: - j = json.loads( message[0:n] ) + j = json.loads(message[0:n].decode('utf8')) except: j = None return j, message[n+1:] - - class timeout(Exception): pass @@ -723,11 +648,11 @@ import json import ssl import time -class SocketPipe: +class SocketPipe: def __init__(self, socket): self.socket = socket - self.message = '' + self.message = b'' self.set_timeout(0.1) self.recv_time = time.time() @@ -757,10 +682,10 @@ class SocketPipe: raise timeout else: print_error("pipe: socket error", err) - data = '' + data = b'' except: traceback.print_exc(file=sys.stderr) - data = '' + data = b'' if not data: # Connection closed remotely return None @@ -769,10 +694,12 @@ class SocketPipe: def send(self, request): out = json.dumps(request) + '\n' + out = out.encode('utf8') self._send(out) def send_all(self, requests): - out = ''.join(map(lambda x: json.dumps(x) + '\n', requests)) + print(requests) + out = b''.join(map(lambda x: (json.dumps(x) + '\n').encode('utf8'), requests)) self._send(out) def _send(self, out): @@ -798,7 +725,6 @@ class SocketPipe: raise e - class QueuePipe: def __init__(self, send_queue=None, get_queue=None): @@ -833,9 +759,8 @@ class QueuePipe: self.send(request) - def check_www_dir(rdir): - import urllib, urlparse, shutil, os + import urllib, shutil, os if not os.path.exists(rdir): os.mkdir(rdir) index = os.path.join(rdir, 'index.html') @@ -850,7 +775,7 @@ def check_www_dir(rdir): "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] for URL in files: - path = urlparse.urlsplit(URL).path + path = urllib_parse.urlsplit(URL).path filename = os.path.basename(path) path = os.path.join(rdir, filename) if not os.path.exists(path): diff --git a/lib/wallet.py b/lib/wallet.py @@ -51,7 +51,7 @@ from .version import * from .keystore import load_keystore, Hardware_KeyStore from .storage import multisig_type -import transaction +from . import transaction from .transaction import Transaction from .plugins import run_hook from . import bitcoin @@ -297,6 +297,7 @@ class Abstract_Wallet(PrintError): self.verifier.merkle_roots.pop(tx_hash, None) # tx will be verified only if height > 0 + print('unverif', tx_hash, tx_height) if tx_hash not in self.verified_tx: self.unverified_tx[tx_hash] = tx_height @@ -759,7 +760,7 @@ class Abstract_Wallet(PrintError): return '' def get_tx_status(self, tx_hash, height, conf, timestamp): - from util import format_time + from .util import format_time if conf == 0: tx = self.transactions.get(tx_hash) if not tx: @@ -1417,16 +1418,11 @@ class Imported_Wallet(Abstract_Wallet): def add_input_sig_info(self, txin, address): addrtype, hash160 = bc_address_to_hash_160(address) - if six.PY3: - x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160) - else: - x_pubkey = 'fd' + bh2u(chr(addrtype) + hash160) + x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160) txin['x_pubkeys'] = [x_pubkey] txin['signatures'] = [None] - - class Deterministic_Wallet(Abstract_Wallet): def __init__(self, storage): @@ -1508,7 +1504,7 @@ class Deterministic_Wallet(Abstract_Wallet): if len(addresses) < limit: self.create_new_address(for_change) continue - if map(lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]: + if list(map(lambda a: self.address_is_old(a), addresses[-limit:] )) == limit*[False]: break else: self.create_new_address(for_change) @@ -1521,7 +1517,7 @@ class Deterministic_Wallet(Abstract_Wallet): else: if len(self.receiving_addresses) != len(self.keystore.keypairs): pubkeys = self.keystore.keypairs.keys() - self.receiving_addresses = map(self.pubkeys_to_address, pubkeys) + self.receiving_addresses = [self.pubkeys_to_address(i) for i in pubkeys] self.save_addresses() for addr in self.receiving_addresses: self.add_address(addr) @@ -1652,7 +1648,7 @@ class P2SH: def pubkeys_to_address(self, pubkey): redeem_script = self.pubkeys_to_redeem_script(pubkey) - return bitcoin.hash160_to_p2sh(hash_160(redeem_script.decode('hex'))) + return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) class Standard_Wallet(Simple_Deterministic_Wallet): @@ -1664,17 +1660,14 @@ class Standard_Wallet(Simple_Deterministic_Wallet): def pubkeys_to_address(self, pubkey): if not self.is_segwit: - return bitcoin.public_key_to_p2pkh(pubkey.decode('hex')) + return bitcoin.public_key_to_p2pkh(bfh(pubkey)) elif bitcoin.TESTNET: redeem_script = self.pubkeys_to_redeem_script(pubkey) - return bitcoin.hash160_to_p2sh(hash_160(redeem_script.decode('hex'))) + return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) else: raise NotImplementedError() - - - class Multisig_Wallet(Deterministic_Wallet, P2SH): # generic m of n gap_limit = 20 diff --git a/lib/x509.py b/lib/x509.py @@ -31,7 +31,7 @@ import six from datetime import datetime import sys from . import util -from .util import profiler, print_error +from .util import profiler, print_error, bh2u import ecdsa import hashlib @@ -74,7 +74,7 @@ class CertificateError(Exception): # helper functions def bitstr_to_bytestr(s): - if s[0] != '\x00': + if s[0] != 0x00: raise BaseException('no padding') return s[1:] @@ -83,14 +83,13 @@ def bytestr_to_int(s): i = 0 for char in s: i <<= 8 - i |= ord(char) + i |= char return i def decode_OID(s): - s = map(ord, s) r = [] - r.append(s[0] / 40) + r.append(s[0] // 40) r.append(s[0] % 40) k = 0 for i in s[1:]: @@ -103,7 +102,7 @@ def decode_OID(s): def encode_OID(oid): - x = map(int, oid.split('.')) + x = [int(i) for i in oid.split('.')] s = chr(x[0] * 40 + x[1]) for i in x[2:]: ss = chr(i % 128) @@ -114,11 +113,11 @@ def encode_OID(oid): return s -class ASN1_Node(str): +class ASN1_Node(bytes): def get_node(self, ix): # return index of first byte, first content byte and last byte. - first = ord(self[ix + 1]) - if (ord(self[ix + 1]) & 0x80) == 0: + first = self[ix + 1] + if (self[ix + 1] & 0x80) == 0: length = first ixf = ix + 2 ixl = ixf + length - 1 @@ -129,72 +128,62 @@ class ASN1_Node(str): ixl = ixf + length - 1 return ix, ixf, ixl - -def root(self): - return self.get_node(0) - - -def next_node(self, node): - ixs, ixf, ixl = node - return self.get_node(ixl + 1) - - -def first_child(self, node): - ixs, ixf, ixl = node - if ord(self[ixs]) & 0x20 != 0x20: - raise BaseException('Can only open constructed types.', hex(ord(self[ixs]))) - return self.get_node(ixf) - - -def is_child_of(node1, node2): - ixs, ixf, ixl = node1 - jxs, jxf, jxl = node2 - return ((ixf <= jxs) and (jxl <= ixl)) or ((jxf <= ixs) and (ixl <= jxl)) - - -def get_all(self, node): - # return type + length + value - ixs, ixf, ixl = node - return self[ixs:ixl + 1] - - -def get_value_of_type(self, node, asn1_type): - # verify type byte and return content - ixs, ixf, ixl = node - if ASN1_TYPES[asn1_type] != ord(self[ixs]): - raise BaseException('Wrong type:', hex(ord(self[ixs])), hex(ASN1_TYPES[asn1_type])) - return self[ixf:ixl + 1] - - -def get_value(self, node): - ixs, ixf, ixl = node - return self[ixf:ixl + 1] - - -def get_children(self, node): - nodes = [] - ii = self.first_child(node) - nodes.append(ii) - while ii[2] < node[2]: - ii = self.next_node(ii) + def root(self): + return self.get_node(0) + + def next_node(self, node): + ixs, ixf, ixl = node + return self.get_node(ixl + 1) + + def first_child(self, node): + ixs, ixf, ixl = node + if self[ixs] & 0x20 != 0x20: + raise BaseException('Can only open constructed types.', hex(self[ixs])) + return self.get_node(ixf) + + def is_child_of(node1, node2): + ixs, ixf, ixl = node1 + jxs, jxf, jxl = node2 + return ((ixf <= jxs) and (jxl <= ixl)) or ((jxf <= ixs) and (ixl <= jxl)) + + def get_all(self, node): + # return type + length + value + ixs, ixf, ixl = node + return self[ixs:ixl + 1] + + def get_value_of_type(self, node, asn1_type): + # verify type byte and return content + ixs, ixf, ixl = node + if ASN1_TYPES[asn1_type] != self[ixs]: + raise BaseException('Wrong type:', hex(self[ixs]), hex(ASN1_TYPES[asn1_type])) + return self[ixf:ixl + 1] + + def get_value(self, node): + ixs, ixf, ixl = node + return self[ixf:ixl + 1] + + def get_children(self, node): + nodes = [] + ii = self.first_child(node) nodes.append(ii) - return nodes - - -def get_sequence(self): - return map(lambda j: self.get_value(j), self.get_children(self.root())) - - -def get_dict(self, node): - p = {} - for ii in self.get_children(node): - for iii in self.get_children(ii): - iiii = self.first_child(iii) - oid = decode_OID(self.get_value_of_type(iiii, 'OBJECT IDENTIFIER')) - iiii = self.next_node(iiii) - value = self.get_value(iiii) - p[oid] = value - return p + while ii[2] < node[2]: + ii = self.next_node(ii) + nodes.append(ii) + return nodes + + def get_sequence(self): + return list(map(lambda j: self.get_value(j), self.get_children(self.root()))) + + def get_dict(self, node): + p = {} + for ii in self.get_children(node): + for iii in self.get_children(ii): + iiii = self.first_child(iii) + oid = decode_OID(self.get_value_of_type(iiii, 'OBJECT IDENTIFIER')) + iiii = self.next_node(iiii) + value = self.get_value(iiii) + p[oid] = value + return p class X509(object): @@ -202,14 +191,14 @@ class X509(object): self.bytes = bytearray(b) - der = ASN1_Node(str(b)) + der = ASN1_Node(b) root = der.root() cert = der.first_child(root) # data for signature self.data = der.get_all(cert) # optional version field - if der.get_value(cert)[0] == chr(0xa0): + if der.get_value(cert)[0] == 0xa0: version = der.first_child(cert) serial_number = der.next_node(version) else: @@ -269,10 +258,10 @@ class X509(object): # Subject Key Identifier r = value.root() value = value.get_value_of_type(r, 'OCTET STRING') - self.SKI = value.encode('hex') + self.SKI = bh2u(value) elif oid == '2.5.29.35': # Authority Key Identifier - self.AKI = value.get_sequence()[0].encode('hex') + self.AKI = bh2u(value.get_sequence()[0]) else: pass @@ -303,8 +292,8 @@ class X509(object): import time now = time.time() TIMESTAMP_FMT = '%y%m%d%H%M%SZ' - not_before = time.mktime(time.strptime(self.notBefore, TIMESTAMP_FMT)) - not_after = time.mktime(time.strptime(self.notAfter, TIMESTAMP_FMT)) + not_before = time.mktime(time.strptime(self.notBefore.decode('ascii'), TIMESTAMP_FMT)) + not_after = time.mktime(time.strptime(self.notAfter.decode('ascii'), TIMESTAMP_FMT)) if not_before > now: raise CertificateError('Certificate has not entered its valid date range. (%s)' % self.get_common_name()) if not_after <= now: @@ -320,7 +309,7 @@ def load_certificates(ca_path): ca_list = {} ca_keyID = {} with open(ca_path, 'rb') as f: - s = f.read().decode('utf8') + s = f.read().decode('ascii') bList = pem.dePemList(s, "CERTIFICATE") for b in bList: try: