electrum

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

commit 5be78950ca31ba903b4bd0f575511eeb732e114c
parent f70408cef5eff3eead108a06ee11a6106253f74d
Author: Dmitry Sorokin <asfins@gmail.com>
Date:   Sun, 22 Jan 2017 21:25:24 +0300

py3

Diffstat:
Melectrum | 41++++++++++++++++++++++++++---------------
Mgui/qt/__init__.py | 16+++++++++-------
Mgui/qt/address_dialog.py | 12+++++++++---
Mgui/qt/address_list.py | 7++++++-
Mgui/qt/amountedit.py | 6++++++
Mgui/qt/console.py | 8+++++++-
Mgui/qt/contact_list.py | 7++++++-
Mgui/qt/fee_slider.py | 6++++++
Mgui/qt/history_list.py | 7++++++-
Mgui/qt/installwizard.py | 14++++++++++----
Mgui/qt/invoice_list.py | 7++++++-
Mgui/qt/main_window.py | 46+++++++++++++++++++++++++---------------------
Mgui/qt/network_dialog.py | 11+++++++++--
Mgui/qt/password_dialog.py | 7++++++-
Mgui/qt/paytoedit.py | 9+++++++--
Mgui/qt/qrcodewidget.py | 8+++++++-
Mgui/qt/qrtextedit.py | 8+++++++-
Mgui/qt/qrwindow.py | 6+++++-
Mgui/qt/request_list.py | 7++++++-
Mgui/qt/seed_dialog.py | 9+++++++--
Mgui/qt/transaction_dialog.py | 7++++++-
Mgui/qt/util.py | 10++++++++--
Mgui/qt/utxo_list.py | 7++++++-
Mgui/stdio.py | 6++++++
Mgui/text.py | 6++++++
Mlib/__init__.py | 28++++++++++++++--------------
Alib/account.py | 0
Mlib/base_wizard.py | 17+++++++++++------
Mlib/bitcoin.py | 240+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mlib/blockchain.py | 11++++++++---
Mlib/coinchooser.py | 13+++++++++----
Mlib/commands.py | 31++++++++++++++++++-------------
Mlib/contacts.py | 14++++++++++----
Mlib/daemon.py | 52++++++++++++++++++++++++++++------------------------
Mlib/dnssec.py | 31++++++++++++++++---------------
Mlib/exchange_rate.py | 17++++++++++++-----
Mlib/i18n.py | 18+++++++++++++-----
Mlib/interface.py | 34++++++++++++++++++++--------------
Mlib/keystore.py | 64+++++++++++++++++++++++++++++++++++-----------------------------
Mlib/mnemonic.py | 21+++++++++++++++------
Mlib/msqr.py | 16+++++++++++-----
Mlib/network.py | 40++++++++++++++++++++++++----------------
Mlib/old_mnemonic.py | 24+++++++++++++++---------
Mlib/paymentrequest.py | 45+++++++++++++++++++++++++++------------------
Mlib/pem.py | 7++++++-
Mlib/plugins.py | 18++++++++++++------
Mlib/rsakey.py | 8++++++--
Mlib/simple_config.py | 21++++++++++++++-------
Mlib/storage.py | 15++++++++++-----
Mlib/synchronizer.py | 12++++++++----
Mlib/tests/test_account.py | 6++++++
Mlib/tests/test_bitcoin.py | 13++++++++++---
Mlib/tests/test_interface.py | 6++++++
Mlib/tests/test_mnemonic.py | 10+++++++++-
Mlib/tests/test_simple_config.py | 17+++++++----------
Mlib/tests/test_transaction.py | 22++++++++++++++++------
Mlib/tests/test_util.py | 6++++++
Mlib/tests/test_wallet.py | 10++++++++--
Mlib/transaction.py | 200+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mlib/util.py | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mlib/verifier.py | 10+++++++---
Mlib/wallet.py | 64++++++++++++++++++++++++++++++++--------------------------------
Mlib/websockets.py | 14++++++++++----
Mlib/x509.py | 213++++++++++++++++++++++++++++++++++++++++++-------------------------------------
64 files changed, 1243 insertions(+), 668 deletions(-)

diff --git a/electrum b/electrum @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- mode: python -*- # # Electrum - lightweight Bitcoin client @@ -23,9 +23,14 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals import os import sys +import six # from https://gist.github.com/tito/09c42fb4767721dc323d import threading @@ -42,6 +47,12 @@ if jnius: jnius.detach() 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 script_dir = os.path.dirname(os.path.realpath(__file__)) is_bundle = getattr(sys, 'frozen', False) @@ -54,6 +65,7 @@ os.environ['KIVY_DATA_DIR'] = os.path.abspath(os.path.dirname(__file__)) + '/gui if is_local or is_android: sys.path.insert(0, os.path.join(script_dir, 'packages')) elif is_bundle and sys.platform=='darwin': + # TODO: py3 sys.path.insert(0, os.getcwd() + "/lib/python2.7/packages") @@ -68,7 +80,7 @@ def check_imports(): import qrcode import pbkdf2 import google.protobuf - import jsonrpclib + # import jsonrpclib except ImportError as e: sys.exit("Error: %s. Try 'sudo pip install <module-name>'"%e.message) # the following imports are for pyinstaller @@ -76,7 +88,7 @@ def check_imports(): from google.protobuf import message from google.protobuf import reflection from google.protobuf import descriptor_pb2 - from jsonrpclib import SimpleJSONRPCServer + # from jsonrpclib import SimpleJSONRPCServer # check that we have the correct version of ecdsa try: from ecdsa.ecdsa import curve_secp256k1, generator_secp256k1 @@ -276,11 +288,11 @@ def run_offline_command(config, config_options): if cmd.requires_network: print_msg("Warning: running command offline") # arguments passed to function - args = map(lambda x: config.get(x), cmd.params) + args = [config.get(x) for x in cmd.params] # decode json arguments - args = map(json_decode, args) + args = list(map(json_decode, args)) # options - args += map(lambda x: (config_options.get(x) if x in ['password', 'new_password'] else config.get(x)), cmd.options) + args += [(config_options.get(x) if x in ['password', 'new_password'] else config.get(x)) for x in cmd.options] cmd_runner = Commands(config, wallet, None) func = getattr(cmd_runner, cmd.name) result = func(*args) @@ -296,10 +308,10 @@ def init_plugins(config, gui_name): if __name__ == '__main__': # on osx, delete Process Serial Number arg generated for apps launched in Finder - sys.argv = filter(lambda x: not x.startswith('-psn'), sys.argv) + sys.argv = list(filter(lambda x: not x.startswith('-psn'), sys.argv)) # old 'help' syntax - if len(sys.argv)>1 and sys.argv[1] == 'help': + if len(sys.argv) > 1 and sys.argv[1] == 'help': sys.argv.remove('help') sys.argv.append('-h') @@ -312,9 +324,9 @@ if __name__ == '__main__': else: raise BaseException('Cannot get argument from stdin') elif arg == '?': - sys.argv[i] = raw_input("Enter argument:") + sys.argv[i] = input("Enter argument:") elif arg == ':': - sys.argv[i] = prompt_password('Enter argument (will not echo):', False) + sys.argv[i] = prompt_password('Enter argument (will noot echo):', False) # parse command line parser = get_parser() @@ -329,9 +341,8 @@ if __name__ == '__main__': } else: config_options = args.__dict__ - for k, v in config_options.items(): - if v is None or (k in config_variables.get(args.cmd, {}).keys()): - config_options.pop(k) + f = lambda key: config_options[key] is not None and key not in config_variables.get(args.cmd, {}).keys() + config_options = {key: config_options[key] for key in filter(f, config_options.keys())} if config_options.get('server'): config_options['auto_connect'] = False @@ -426,8 +437,8 @@ if __name__ == '__main__': init_plugins(config, 'cmdline') result = run_offline_command(config, config_options) - # print result - if type(result) in [str, unicode]: + # print result + if isinstance(result, six.text_type): print_msg(result) elif type(result) is dict and result.get('error'): print_stderr(result.get('error')) diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py @@ -43,18 +43,20 @@ from electrum.synchronizer import Synchronizer from electrum.verifier import SPV from electrum.util import DebugMem, UserCancelled, InvalidPassword from electrum.wallet import Abstract_Wallet -from installwizard import InstallWizard, GoBack + +from .installwizard import InstallWizard, GoBack try: - import icons_rc -except Exception: - print "Error: Could not find icons file." - print "Please run 'pyrcc4 icons.qrc -o gui/qt/icons_rc.py', and reinstall Electrum" + from . import icons_rc +except Exception as e: + print(e) + print("Error: Could not find icons file.") + print("Please run 'pyrcc4 icons.qrc -o gui/qt/icons_rc.py', and reinstall Electrum") sys.exit(1) -from util import * # * needed for plugins -from main_window import ElectrumWindow +from .util import * # * needed for plugins +from .main_window import ElectrumWindow class OpenFileEventFilter(QObject): diff --git a/gui/qt/address_dialog.py b/gui/qt/address_dialog.py @@ -22,16 +22,22 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from electrum.i18n import _ import PyQt4 from PyQt4.QtGui import * from PyQt4.QtCore import * -from util import * -from history_list import HistoryList -from qrtextedit import ShowQRTextEdit +from .util import * +from .history_list import HistoryList +from .qrtextedit import ShowQRTextEdit + class AddressDialog(WindowModalDialog): diff --git a/gui/qt/address_list.py b/gui/qt/address_list.py @@ -22,11 +22,16 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import webbrowser -from util import * +from .util import * from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time from electrum.plugins import run_hook diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py @@ -1,11 +1,17 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from PyQt4.QtCore import * from PyQt4.QtGui import * from decimal import Decimal from electrum.util import format_satoshis_plain + class MyLineEdit(QLineEdit): frozen = pyqtSignal() diff --git a/gui/qt/console.py b/gui/qt/console.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six # source: http://stackoverflow.com/questions/2758159/how-to-embed-a-python-interpreter-in-a-pyqt-widget import sys, os, re @@ -220,7 +226,7 @@ class Console(QtGui.QPlainTextEdit): self.appendPlainText(repr(result)) except SyntaxError: # exec is generally considered bad practice. use it wisely! - exec command in self.namespace + exec(command) in self.namespace except SystemExit: self.close() except Exception: diff --git a/gui/qt/contact_list.py b/gui/qt/contact_list.py @@ -22,8 +22,13 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals import webbrowser +import six from electrum.i18n import _ from electrum.bitcoin import is_address @@ -32,7 +37,7 @@ from electrum.plugins import run_hook from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED from PyQt4.QtGui import * from PyQt4.QtCore import * -from util import MyTreeWidget, pr_tooltips, pr_icons +from .util import MyTreeWidget, pr_tooltips, pr_icons class ContactList(MyTreeWidget): diff --git a/gui/qt/fee_slider.py b/gui/qt/fee_slider.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six from electrum.i18n import _ import PyQt4 diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py @@ -22,11 +22,16 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import webbrowser -from util import * +from .util import * from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time from electrum.plugins import run_hook diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import sys import os @@ -11,10 +17,10 @@ from electrum.util import UserCancelled, InvalidPassword from electrum.base_wizard import BaseWizard from electrum.i18n import _ -from seed_dialog import SeedLayout, KeysLayout -from network_dialog import NetworkChoiceLayout -from util import * -from password_dialog import PasswordLayout, PW_NEW +from .seed_dialog import SeedLayout, KeysLayout +from .network_dialog import NetworkChoiceLayout +from .util import * +from .password_dialog import PasswordLayout, PW_NEW class GoBack(Exception): diff --git a/gui/qt/invoice_list.py b/gui/qt/invoice_list.py @@ -22,9 +22,14 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six -from util import * +from .util import * from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time from electrum.plugins import run_hook diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import sys, time, threading import os, json, traceback import shutil @@ -39,7 +44,7 @@ from PyQt4.QtGui import * from PyQt4.QtCore import * import PyQt4.QtCore as QtCore -import icons_rc +from . import icons_rc from electrum import keystore from electrum.bitcoin import COIN, is_valid, TYPE_ADDRESS @@ -57,17 +62,17 @@ try: except: plot_history = None -from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, BTCkBEdit -from qrcodewidget import QRCodeWidget, QRDialog -from qrtextedit import ShowQRTextEdit -from transaction_dialog import show_transaction -from fee_slider import FeeSlider +from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, BTCkBEdit +from .qrcodewidget import QRCodeWidget, QRDialog +from .qrtextedit import ShowQRTextEdit +from .transaction_dialog import show_transaction +from .fee_slider import FeeSlider from electrum import ELECTRUM_VERSION import re -from util import * +from .util import * class StatusBarButton(QPushButton): @@ -397,7 +402,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): try: shutil.copy2(path, new_path) self.show_message(_("A copy of your wallet file was created in")+" '%s'" % str(new_path), title=_("Wallet backup created")) - except (IOError, os.error), reason: + except (IOError, os.error) as reason: self.show_critical(_("Electrum was unable to copy your wallet file to the specified location.") + "\n" + str(reason), title=_("Unable to create backup")) def update_recently_visited(self, filename): @@ -722,13 +727,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.update_completions() def create_history_tab(self): - from history_list import HistoryList + from .history_list import HistoryList self.history_list = l = HistoryList(self) l.searchable_list = l return l def show_address(self, addr): - import address_dialog + from . import address_dialog d = address_dialog.AddressDialog(self, addr) d.exec_() @@ -770,7 +775,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.connect_fields(self, self.receive_amount_e, self.fiat_receive_e, None) self.expires_combo = QComboBox() - self.expires_combo.addItems(map(lambda x:x[0], expiration_values)) + self.expires_combo.addItems([i[0] for i in expiration_values]) self.expires_combo.setCurrentIndex(3) self.expires_combo.setFixedWidth(self.receive_amount_e.width()) msg = ' '.join([ @@ -806,7 +811,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.receive_requests_label = QLabel(_('Requests')) - from request_list import RequestList + from .request_list import RequestList self.request_list = RequestList(self) # layout @@ -991,7 +996,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): grid.setSpacing(8) grid.setColumnStretch(3, 1) - from paytoedit import PayToEdit + from .paytoedit import PayToEdit self.amount_e = BTCAmountEdit(self.get_decimal_point) self.payto_e = PayToEdit(self) msg = _('Recipient of the funds.') + '\n\n'\ @@ -1123,7 +1128,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.fee_e.textChanged.connect(entry_changed) self.invoices_label = QLabel(_('Invoices')) - from invoice_list import InvoiceList + from .invoice_list import InvoiceList self.invoice_list = InvoiceList(self) vbox0 = QVBoxLayout() @@ -1567,17 +1572,17 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): return w def create_addresses_tab(self): - from address_list import AddressList + from .address_list import AddressList self.address_list = l = AddressList(self) return self.create_list_tab(l) def create_utxo_tab(self): - from utxo_list import UTXOList + from .utxo_list import UTXOList self.utxo_list = l = UTXOList(self) return self.create_list_tab(l) def create_contacts_tab(self): - from contact_list import ContactList + from .contact_list import ContactList self.contact_list = l = ContactList(self) return self.create_list_tab(l) @@ -1693,11 +1698,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.payment_request_error() def create_console_tab(self): - from console import Console + from .console import Console self.console = console = Console() return console - def update_console(self): console = self.console console.history = self.config.get("console-history",[]) @@ -2202,7 +2206,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): with open(fileName, 'w+') as f: json.dump(labels, f, indent=4, sort_keys=True) self.show_message(_("Your labels were exported to") + " '%s'" % str(fileName)) - except (IOError, os.error), reason: + except (IOError, os.error) as reason: self.show_critical(_("Electrum was unable to export your labels.") + "\n" + str(reason)) @@ -2226,7 +2230,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): return try: self.do_export_history(self.wallet, filename, csv_button.isChecked()) - except (IOError, os.error), reason: + except (IOError, os.error) as reason: export_error_label = _("Electrum was unable to produce a transaction export.") self.show_critical(export_error_label + "\n" + str(reason), title=_("Unable to export history")) return diff --git a/gui/qt/network_dialog.py b/gui/qt/network_dialog.py @@ -22,8 +22,15 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import socket + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import socket +import six from PyQt4.QtGui import * from PyQt4.QtCore import * import PyQt4.QtCore as QtCore @@ -32,7 +39,7 @@ from electrum.i18n import _ from electrum.network import DEFAULT_PORTS from electrum.network import serialize_server, deserialize_server -from util import * +from .util import * protocol_names = ['TCP', 'SSL'] protocol_letters = 'ts' diff --git a/gui/qt/password_dialog.py b/gui/qt/password_dialog.py @@ -22,11 +22,16 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from PyQt4.QtGui import * from PyQt4.QtCore import * from electrum.i18n import _ -from util import * +from .util import * import re import math diff --git a/gui/qt/paytoedit.py b/gui/qt/paytoedit.py @@ -22,16 +22,21 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from PyQt4.QtCore import * from PyQt4.QtGui import * -from qrtextedit import ScanQRTextEdit +from .qrtextedit import ScanQRTextEdit import re from decimal import Decimal from electrum import bitcoin -import util +from . import util RE_ADDRESS = '[1-9A-HJ-NP-Za-km-z]{26,}' RE_ALIAS = '(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>' diff --git a/gui/qt/qrcodewidget.py b/gui/qt/qrcodewidget.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six from PyQt4.QtGui import * from PyQt4.QtCore import * import PyQt4.QtGui as QtGui @@ -7,7 +13,7 @@ import qrcode import electrum from electrum.i18n import _ -from util import WindowModalDialog +from .util import WindowModalDialog class QRCodeWidget(QWidget): diff --git a/gui/qt/qrtextedit.py b/gui/qt/qrtextedit.py @@ -1,9 +1,15 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six from electrum.i18n import _ from electrum.plugins import run_hook from PyQt4.QtGui import * from PyQt4.QtCore import * -from util import ButtonsTextEdit, MessageBoxMixin +from .util import ButtonsTextEdit, MessageBoxMixin class ShowQRTextEdit(ButtonsTextEdit): diff --git a/gui/qt/qrwindow.py b/gui/qt/qrwindow.py @@ -22,11 +22,15 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import re import platform from decimal import Decimal -from urllib import quote from PyQt4.QtGui import * from PyQt4.QtCore import * diff --git a/gui/qt/request_list.py b/gui/qt/request_list.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time, age @@ -30,7 +35,7 @@ from electrum.plugins import run_hook from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED from PyQt4.QtGui import * from PyQt4.QtCore import * -from util import MyTreeWidget, pr_tooltips, pr_icons +from .util import MyTreeWidget, pr_tooltips, pr_icons class RequestList(MyTreeWidget): diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py @@ -22,13 +22,18 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from PyQt4.QtGui import * from PyQt4.QtCore import * from electrum.i18n import _ -from util import * -from qrtextedit import ShowQRTextEdit, ScanQRTextEdit +from .util import * +from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit def seed_warning_msg(seed): diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import copy import datetime import json @@ -37,7 +42,7 @@ from electrum.bitcoin import base_encode from electrum.i18n import _ from electrum.plugins import run_hook -from util import * +from .util import * dialogs = [] # Otherwise python randomly garbage collects the dialogs... diff --git a/gui/qt/util.py b/gui/qt/util.py @@ -1,10 +1,16 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import os.path import time import traceback import sys import threading import platform -import Queue +from six.moves import queue from collections import namedtuple from functools import partial @@ -565,7 +571,7 @@ class TaskThread(QThread): def __init__(self, parent, on_error=None): super(TaskThread, self).__init__(parent) self.on_error = on_error - self.tasks = Queue.Queue() + self.tasks = queue.Queue() self.doneSig.connect(self.on_done) self.start() diff --git a/gui/qt/utxo_list.py b/gui/qt/utxo_list.py @@ -22,8 +22,13 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals -from util import * +import six +from .util import * from electrum.i18n import _ from electrum.bitcoin import is_address diff --git a/gui/stdio.py b/gui/stdio.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six from decimal import Decimal _ = lambda x:x #from i18n import _ diff --git a/gui/text.py b/gui/text.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import tty, sys import curses, datetime, locale from decimal import Decimal diff --git a/lib/__init__.py b/lib/__init__.py @@ -1,14 +1,14 @@ -from version import ELECTRUM_VERSION -from util import format_satoshis, print_msg, print_error, set_verbosity -from wallet import Synchronizer, Wallet, Imported_Wallet -from storage import WalletStorage -from coinchooser import COIN_CHOOSERS -from network import Network, pick_random_server -from interface import Connection, Interface -from simple_config import SimpleConfig, get_config, set_config -import bitcoin -import transaction -import daemon -from transaction import Transaction -from plugins import BasePlugin -from commands import Commands, known_commands +from .version import ELECTRUM_VERSION +from .util import format_satoshis, print_msg, print_error, set_verbosity +from .wallet import Synchronizer, Wallet, Imported_Wallet +from .storage import WalletStorage +from .coinchooser import COIN_CHOOSERS +from .network import Network, pick_random_server +from .interface import Connection, Interface +from .simple_config import SimpleConfig, get_config, set_config +from . import bitcoin +from . import transaction +from . import daemon +from .transaction import Transaction +from .plugins import BasePlugin +from .commands import Commands, known_commands diff --git a/lib/account.py b/lib/account.py diff --git a/lib/base_wizard.py b/lib/base_wizard.py @@ -22,14 +22,19 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import os -import bitcoin -import keystore -from keystore import bip44_derivation -from wallet import Wallet, Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types -from i18n import _ -from plugins import run_hook +from . import bitcoin +from . import keystore +from .keystore import bip44_derivation +from .wallet import Wallet, Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types +from .i18n import _ +from .plugins import run_hook class BaseWizard(object): diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -26,12 +26,12 @@ import hashlib import base64 -import os import re import hmac -import version -from util import print_error, InvalidPassword +from lib.util import bfh, bh2u +from . import version +from .util import print_error, InvalidPassword, assert_bytes, _bytes, to_bytes import ecdsa import pyaes @@ -98,6 +98,9 @@ except: AES = None def aes_encrypt_with_iv(key, iv, data): + assert_bytes(key, iv, data) + if six.PY2: + key, iv, data = map(str, (key, iv, data)) if AES: padlen = 16 - (len(data) % 16) if padlen == 0: @@ -112,6 +115,9 @@ def aes_encrypt_with_iv(key, iv, data): return e def aes_decrypt_with_iv(key, iv, data): + assert_bytes(key, iv, data) + if six.PY2: + key, iv, data = map(str, (key, iv, data)) if AES: cipher = AES.new(key, AES.MODE_CBC, iv) data = cipher.decrypt(data) @@ -127,14 +133,21 @@ def aes_decrypt_with_iv(key, iv, data): return s def EncodeAES(secret, s): - iv = bytes(os.urandom(16)) + assert_bytes(s) + iv = _bytes(os.urandom(16)) + # aes_cbc = pyaes.AESModeOfOperationCBC(secret, iv=iv) + # aes = pyaes.Encrypter(aes_cbc) + # e = iv + aes.feed(s) + aes.feed() ct = aes_encrypt_with_iv(secret, iv, s) e = iv + ct return base64.b64encode(e) def DecodeAES(secret, e): - e = bytes(base64.b64decode(e)) + e = _bytes(base64.b64decode(e)) iv, e = e[:16], e[16:] + # aes_cbc = pyaes.AESModeOfOperationCBC(secret, iv=iv) + # aes = pyaes.Decrypter(aes_cbc) + # s = aes.feed(e) + aes.feed() s = aes_decrypt_with_iv(secret, iv, e) return s @@ -158,10 +171,11 @@ def pw_decode(s, password): def rev_hex(s): - return s.decode('hex')[::-1].encode('hex') + return bh2u(bfh(s)[::-1]) def int_to_hex(i, length=1): + assert isinstance(i, int) s = hex(i)[2:].rstrip('L') s = "0"*(2*length - len(s)) + s return rev_hex(s) @@ -191,26 +205,30 @@ def op_push(i): def sha256(x): - return hashlib.sha256(x).digest() + x = to_bytes(x, 'utf8') + return _bytes(hashlib.sha256(x).digest()) def Hash(x): - if type(x) is unicode: x=x.encode('utf-8') - return sha256(sha256(x)) + x = to_bytes(x, 'utf8') + out = _bytes(sha256(sha256(x))) + return out + + +hash_encode = lambda x: bh2u(x[::-1]) +hash_decode = lambda x: bfh(x)[::-1] +hmac_sha_512 = lambda x, y: _bytes(hmac.new(x, y, hashlib.sha512).digest()) -hash_encode = lambda x: x[::-1].encode('hex') -hash_decode = lambda x: x.decode('hex')[::-1] -hmac_sha_512 = lambda x,y: hmac.new(x, y, hashlib.sha512).digest() def is_new_seed(x, prefix=version.SEED_PREFIX): - import mnemonic + from . import mnemonic x = mnemonic.normalize_text(x) - s = hmac_sha_512("Seed version", x.encode('utf8')).encode('hex') + s = bh2u(hmac_sha_512(b"Seed version", x.encode('utf8'))) return s.startswith(prefix) def is_old_seed(seed): - import old_mnemonic + from . import old_mnemonic words = seed.strip().split() try: old_mnemonic.mn_decode(words) @@ -218,8 +236,8 @@ def is_old_seed(seed): except Exception: uses_electrum_words = False try: - seed.decode('hex') - is_hex = (len(seed) == 32 or len(seed) == 64) + seed = bfh(seed) + is_hex = (len(seed) == 16 or len(seed) == 32) except Exception: is_hex = False return is_hex or (uses_electrum_words and (len(words) == 12 or len(words) == 24)) @@ -255,37 +273,38 @@ def i2o_ECPublicKey(pubkey, compressed=False): '%064x' % pubkey.point.x() + \ '%064x' % pubkey.point.y() - return key.decode('hex') - + return bfh(key) # end pywallet openssl private key implementation - ############ functions from pywallet ##################### - def hash_160(public_key): if 'ANDROID_DATA' in os.environ: from Crypto.Hash import RIPEMD md = RIPEMD.new() else: md = hashlib.new('ripemd') + public_key = to_bytes(public_key, 'ascii') md.update(sha256(public_key)) return md.digest() def hash_160_to_bc_address(h160, addrtype, witness_program_version=1): - s = chr(addrtype) + s = bytes([addrtype]) if addrtype == ADDRTYPE_P2WPKH: - s += chr(witness_program_version) + chr(0) + s += bytes([witness_program_version]) + b'\x00' s += h160 return base_encode(s+Hash(s)[0:4], base=58) + def bc_address_to_hash_160(addr): - bytes = base_decode(addr, 25, base=58) - return ord(bytes[0]), bytes[1:21] + addr = to_bytes(addr, 'ascii') + _bytes = base_decode(addr, 25, base=58) + return _bytes[0], _bytes[1:21] def hash160_to_p2pkh(h160): return hash_160_to_bc_address(h160, ADDRTYPE_P2PKH) + def hash160_to_p2sh(h160): return hash_160_to_bc_address(h160, ADDRTYPE_P2SH) @@ -298,60 +317,70 @@ def public_key_to_p2wpkh(public_key): -__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' +__b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' assert len(__b58chars) == 58 -__b43chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:' +__b43chars = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:' assert len(__b43chars) == 43 def base_encode(v, base): """ encode v, which is a string of bytes, to base58.""" - if base == 58: - chars = __b58chars - elif base == 43: + assert_bytes(v) + assert base in (58, 43) + chars = __b58chars + if base == 43: chars = __b43chars - long_value = 0L + long_value = 0 for (i, c) in enumerate(v[::-1]): - long_value += (256**i) * ord(c) - result = '' + long_value += (256**i) * c + result = bytearray() while long_value >= base: div, mod = divmod(long_value, base) - result = chars[mod] + result + result.append(chars[mod]) long_value = div - result = chars[long_value] + result + result.append(chars[long_value]) # Bitcoin does a little leading-zero-compression: # leading 0-bytes in the input become leading-1s nPad = 0 for c in v: - if c == '\0': nPad += 1 - else: break - return (chars[0]*nPad) + result + if c == 0x00: + nPad += 1 + else: + break + result.extend([chars[0]] * nPad) + result.reverse() + return result.decode('ascii') def base_decode(v, length, base): """ decode v into a string of len bytes.""" - if base == 58: - chars = __b58chars - elif base == 43: + # assert_bytes(v) + v = to_bytes(v, 'ascii') + assert base in (58, 43) + chars = __b58chars + if base == 43: chars = __b43chars - long_value = 0L + long_value = 0 for (i, c) in enumerate(v[::-1]): - long_value += chars.find(c) * (base**i) - result = '' + long_value += chars.find(_bytes([c])) * (base**i) + result = bytearray() while long_value >= 256: div, mod = divmod(long_value, 256) - result = chr(mod) + result + result.append(mod) long_value = div - result = chr(long_value) + result + result.append(long_value) nPad = 0 for c in v: - if c == chars[0]: nPad += 1 - else: break - result = chr(0)*nPad + result + if c == chars[0]: + nPad += 1 + else: + break + result.extend(b'\x00' * nPad) if length is not None and len(result) != length: return None - return result + result.reverse() + return bytes(result) def EncodeBase58Check(vchIn): @@ -377,14 +406,15 @@ def PrivKeyToSecret(privkey): def SecretToASecret(secret, compressed=False): addrtype = ADDRTYPE_P2PKH - vchIn = chr((addrtype+128)&255) + secret - if compressed: vchIn += '\01' + vchIn = bytes([(addrtype+128)&255]) + secret + if compressed: vchIn += b'\01' return EncodeBase58Check(vchIn) + def ASecretToSecret(key): addrtype = ADDRTYPE_P2PKH vch = DecodeBase58Check(key) - if vch and vch[0] == chr((addrtype+128)&255): + if vch and vch[0] == ((addrtype+128)&255): return vch[1:] elif is_minikey(key): return minikey_to_private_key(key) @@ -404,7 +434,7 @@ def GetPubKey(pubkey, compressed=False): def GetSecret(pkey): - return ('%064x' % pkey.secret).decode('hex') + return bfh('%064x' % pkey.secret) def is_compressed(sec): @@ -418,12 +448,12 @@ def public_key_from_private_key(sec): assert pkey compressed = is_compressed(sec) public_key = GetPubKey(pkey.pubkey, compressed) - return public_key.encode('hex') + return bh2u(public_key) def address_from_private_key(sec): public_key = public_key_from_private_key(sec) - address = public_key_to_p2pkh(public_key.decode('hex')) + address = public_key_to_p2pkh(bfh(public_key)) return address @@ -434,7 +464,7 @@ def is_valid(addr): def is_address(addr): try: addrtype, h = bc_address_to_hash_160(addr) - except Exception: + except Exception as e: return False if addrtype not in [ADDRTYPE_P2PKH, ADDRTYPE_P2SH]: return False @@ -478,10 +508,14 @@ from ecdsa.curves import SECP256k1 from ecdsa.ellipticcurve import Point from ecdsa.util import string_to_number, number_to_string + def msg_magic(message): varint = var_int(len(message)) - encoded_varint = "".join([chr(int(varint[i:i+2], 16)) for i in xrange(0, len(varint), 2)]) - return "\x18Bitcoin Signed Message:\n" + encoded_varint + message + if six.PY3: + encoded_varint = varint.encode('ascii') + else: + encoded_varint = b"".join([chr(int(varint[i:i+2], 16)) for i in range(0, len(varint), 2)]) + return b"\x18Bitcoin Signed Message:\n" + encoded_varint + message def verify_message(address, sig, message): @@ -502,11 +536,11 @@ def verify_message(address, sig, message): def encrypt_message(message, pubkey): - return EC_KEY.encrypt_message(message, pubkey.decode('hex')) + return EC_KEY.encrypt_message(message, bfh(pubkey)) def chunks(l, n): - return [l[i:i+n] for i in xrange(0, len(l), n)] + return [l[i:i+n] for i in range(0, len(l), n)] def ECC_YfromX(x,curved=curve_secp256k1, odd=True): @@ -516,7 +550,7 @@ def ECC_YfromX(x,curved=curve_secp256k1, odd=True): for offset in range(128): Mx = x + offset My2 = pow(Mx, 3, _p) + _a * pow(Mx, 2, _p) + _b % _p - My = pow(My2, (_p+1)/4, _p ) + My = pow(My2, (_p+1)//4, _p ) if curved.contains_point(Mx,My): if odd == bool(My&1): @@ -531,20 +565,19 @@ def negative_point(P): def point_to_ser(P, comp=True ): if comp: - return ( ('%02x'%(2+(P.y()&1)))+('%064x'%P.x()) ).decode('hex') - return ( '04'+('%064x'%P.x())+('%064x'%P.y()) ).decode('hex') + return bfh( ('%02x'%(2+(P.y()&1)))+('%064x'%P.x()) ) + return bfh( '04'+('%064x'%P.x())+('%064x'%P.y()) ) def ser_to_point(Aser): curve = curve_secp256k1 generator = generator_secp256k1 _r = generator.order() - assert Aser[0] in ['\x02','\x03','\x04'] - if Aser[0] == '\x04': + assert Aser[0] in [0x02, 0x03, 0x04] + if Aser[0] == 0x04: return Point( curve, string_to_number(Aser[1:33]), string_to_number(Aser[33:]), _r ) Mx = string_to_number(Aser[1:]) - return Point( curve, Mx, ECC_YfromX(Mx, curve, Aser[0]=='\x03')[0], _r ) - + return Point( curve, Mx, ECC_YfromX(Mx, curve, Aser[0] == 0x03)[0], _r ) class MyVerifyingKey(ecdsa.VerifyingKey): @@ -552,14 +585,14 @@ class MyVerifyingKey(ecdsa.VerifyingKey): def from_signature(klass, sig, recid, h, curve): """ See http://www.secg.org/download/aid-780/sec1-v2.pdf, chapter 4.1.6 """ from ecdsa import util, numbertheory - import msqr + from . import msqr curveFp = curve.curve G = curve.generator order = G.order() # extract r,s from signature r, s = util.sigdecode_string(sig, order) # 1.1 - x = r + (recid/2) * order + x = r + (recid//2) * order # 1.3 alpha = ( x * x * x + curveFp.a() * x + curveFp.b() ) % curveFp.p() beta = msqr.modular_sqrt(alpha, curveFp.p()) @@ -578,7 +611,7 @@ class MyVerifyingKey(ecdsa.VerifyingKey): def pubkey_from_signature(sig, h): if len(sig) != 65: raise Exception("Wrong encoding") - nV = ord(sig[0]) + nV = sig[0] if nV < 27 or nV >= 35: raise Exception("Bad encoding") if nV >= 31: @@ -598,7 +631,7 @@ class MySigningKey(ecdsa.SigningKey): G = curve.generator order = G.order() r, s = ecdsa.SigningKey.sign_number(self, number, entropy, k) - if s > order/2: + if s > order//2: s = order - s return r, s @@ -612,7 +645,7 @@ class EC_KEY(object): self.secret = secret def get_public_key(self, compressed=True): - return point_to_ser(self.pubkey.point, compressed).encode('hex') + return bh2u(point_to_ser(self.pubkey.point, compressed)) def sign(self, msg_hash): private_key = MySigningKey.from_secret_exponent(self.secret, curve = SECP256k1) @@ -622,19 +655,20 @@ class EC_KEY(object): return signature def sign_message(self, message, is_compressed): + message = to_bytes(message, 'utf8') signature = self.sign(Hash(msg_magic(message))) for i in range(4): - sig = chr(27 + i + (4 if is_compressed else 0)) + signature + sig = bytes([27 + i + (4 if is_compressed else 0)]) + signature try: self.verify_message(sig, message) return sig - except Exception: + except Exception as e: continue else: raise Exception("error: cannot sign message") - def verify_message(self, sig, message): + assert_bytes(message) h = Hash(msg_magic(message)) public_key, compressed = pubkey_from_signature(sig, h) # check public key @@ -648,6 +682,7 @@ class EC_KEY(object): @classmethod def encrypt_message(self, message, pubkey): + assert_bytes(message) pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): @@ -659,13 +694,12 @@ class EC_KEY(object): key = hashlib.sha512(ecdh_key).digest() iv, key_e, key_m = key[0:16], key[16:32], key[32:] ciphertext = aes_encrypt_with_iv(key_e, iv, message) - ephemeral_pubkey = ephemeral.get_public_key(compressed=True).decode('hex') - encrypted = 'BIE1' + ephemeral_pubkey + ciphertext + ephemeral_pubkey = bfh(ephemeral.get_public_key(compressed=True)) + encrypted = b'BIE1' + ephemeral_pubkey + ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() return base64.b64encode(encrypted + mac) - def decrypt_message(self, encrypted): encrypted = base64.b64decode(encrypted) if len(encrypted) < 85: @@ -674,11 +708,11 @@ class EC_KEY(object): ephemeral_pubkey = encrypted[4:37] ciphertext = encrypted[37:-32] mac = encrypted[-32:] - if magic != 'BIE1': + if magic != b'BIE1': raise Exception('invalid ciphertext: invalid magic bytes') try: ephemeral_pubkey = ser_to_point(ephemeral_pubkey) - except AssertionError, e: + except AssertionError as e: raise Exception('invalid ciphertext: invalid ephemeral pubkey') if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, ephemeral_pubkey.x(), ephemeral_pubkey.y()): raise Exception('invalid ciphertext: invalid ephemeral pubkey') @@ -715,13 +749,14 @@ def get_pubkeys_from_secret(secret): # public key can be determined without the master private key. def CKD_priv(k, c, n): is_prime = n & BIP32_PRIME - return _CKD_priv(k, c, rev_hex(int_to_hex(n,4)).decode('hex'), is_prime) + return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n,4))), is_prime) + def _CKD_priv(k, c, s, is_prime): order = generator_secp256k1.order() keypair = EC_KEY(k) cK = GetPubKey(keypair.pubkey,True) - data = chr(0) + k + s if is_prime else cK + s + data = bytes([0]) + k + s if is_prime else cK + s I = hmac.new(c, data, hashlib.sha512).digest() k_n = number_to_string( (string_to_number(I[0:32]) + string_to_number(k)) % order , order ) c_n = I[32:] @@ -735,7 +770,7 @@ def _CKD_priv(k, c, s, is_prime): # non-negative. If n is negative, we need the master private key to find it. def CKD_pub(cK, c, n): if n & BIP32_PRIME: raise - return _CKD_pub(cK, c, rev_hex(int_to_hex(n,4)).decode('hex')) + return _CKD_pub(cK, c, bfh(rev_hex(int_to_hex(n,4)))) # helper function, callable with arbitrary string def _CKD_pub(cK, c, s): @@ -750,41 +785,48 @@ def _CKD_pub(cK, c, s): def xprv_header(xtype): - return ("%08x"%(XPRV_HEADER + xtype)).decode('hex') + return bfh("%08x" % (XPRV_HEADER + xtype)) + def xpub_header(xtype): - return ("%08x"%(XPUB_HEADER + xtype)).decode('hex') + return bfh("%08x" % (XPUB_HEADER + xtype)) + -def serialize_xprv(xtype, c, k, depth=0, fingerprint=chr(0)*4, child_number=chr(0)*4): - xprv = xprv_header(xtype) + chr(depth) + fingerprint + child_number + c + chr(0) + k +def serialize_xprv(xtype, c, k, depth=0, fingerprint=b'\x00'*4, child_number=b'\x00'*4): + xprv = xprv_header(xtype) + bytes([depth]) + fingerprint + child_number + c + bytes([0]) + k return EncodeBase58Check(xprv) -def serialize_xpub(xtype, c, cK, depth=0, fingerprint=chr(0)*4, child_number=chr(0)*4): - xpub = xpub_header(xtype) + chr(depth) + fingerprint + child_number + c + cK + +def serialize_xpub(xtype, c, cK, depth=0, fingerprint=b'\x00'*4, child_number=b'\x00'*4): + xpub = xpub_header(xtype) + bytes([depth]) + fingerprint + child_number + c + cK return EncodeBase58Check(xpub) + def deserialize_xkey(xkey, prv): xkey = DecodeBase58Check(xkey) if len(xkey) != 78: raise BaseException('Invalid length') - depth = ord(xkey[4]) + depth = xkey[4] fingerprint = xkey[5:9] child_number = xkey[9:13] c = xkey[13:13+32] header = XPRV_HEADER if prv else XPUB_HEADER - xtype = int('0x' + xkey[0:4].encode('hex'), 16) - header + xtype = int('0x' + bh2u(xkey[0:4]), 16) - header if xtype not in ([0, 1] if TESTNET else [0]): raise BaseException('Invalid header') n = 33 if prv else 32 K_or_k = xkey[13+n:] return xtype, depth, fingerprint, child_number, c, K_or_k + def deserialize_xpub(xkey): return deserialize_xkey(xkey, False) + def deserialize_xprv(xkey): return deserialize_xkey(xkey, True) + def is_xpub(text): try: deserialize_xpub(text) @@ -792,6 +834,7 @@ def is_xpub(text): except: return False + def is_xprv(text): try: deserialize_xprv(text) @@ -807,7 +850,7 @@ def xpub_from_xprv(xprv): def bip32_root(seed, xtype): - I = hmac.new("Bitcoin seed", seed, hashlib.sha512).digest() + I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest() master_k = I[0:32] master_c = I[32:] K, cK = get_pubkeys_from_secret(master_k) @@ -815,9 +858,10 @@ def bip32_root(seed, xtype): xpub = serialize_xpub(xtype, master_c, cK) return xprv, xpub + def xpub_from_pubkey(xtype, cK): - assert cK[0] in ['\x02','\x03'] - return serialize_xpub(xtype, chr(0)*32, cK) + assert cK[0] in [0x02, 0x03] + return serialize_xpub(xtype, b'\x00'*32, cK) def bip32_derivation(s): @@ -849,7 +893,7 @@ def bip32_private_derivation(xprv, branch, sequence): depth += 1 _, parent_cK = get_pubkeys_from_secret(parent_k) fingerprint = hash_160(parent_cK)[0:4] - child_number = ("%08X"%i).decode('hex') + child_number = bfh("%08X"%i) K, cK = get_pubkeys_from_secret(k) xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) xprv = serialize_xprv(xtype, c, k, depth, fingerprint, child_number) @@ -867,7 +911,7 @@ def bip32_public_derivation(xpub, branch, sequence): cK, c = CKD_pub(cK, c, i) depth += 1 fingerprint = hash_160(parent_cK)[0:4] - child_number = ("%08X"%i).decode('hex') + child_number = bfh("%08X"%i) return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) @@ -878,7 +922,7 @@ def bip32_private_key(sequence, k, chain): def xkeys_from_seed(seed, passphrase, derivation): - from mnemonic import Mnemonic + from .mnemonic import Mnemonic xprv, xpub = bip32_root(Mnemonic.mnemonic_to_seed(seed, passphrase), 0) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation) return xprv, xpub diff --git a/lib/blockchain.py b/lib/blockchain.py @@ -22,15 +22,20 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import os -import util import threading -import bitcoin -from bitcoin import * +from . import util +from . import bitcoin +from .bitcoin import * MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 diff --git a/lib/coinchooser.py b/lib/coinchooser.py @@ -22,13 +22,19 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from collections import defaultdict, namedtuple from math import floor, log10 -from bitcoin import sha256, COIN, TYPE_ADDRESS -from transaction import Transaction -from util import NotEnoughFunds, PrintError, profiler +from .bitcoin import sha256, COIN, TYPE_ADDRESS +from .transaction import Transaction +from .util import NotEnoughFunds, PrintError, profiler + # A simple deterministic PRNG. Used to deterministically shuffle a # set of coins - the same set of coins should produce the same output. @@ -36,7 +42,6 @@ from util import NotEnoughFunds, PrintError, profiler # so if sending twice from the same UTXO set we choose the same UTXOs # to spend. This prevents attacks on users by malicious or stale # servers. - class PRNG: def __init__(self, seed): self.sha = sha256(seed) diff --git a/lib/commands.py b/lib/commands.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import os import sys import datetime @@ -35,15 +40,16 @@ import base64 from functools import wraps from decimal import Decimal -import util -from util import print_msg, format_satoshis, print_stderr -import bitcoin -from bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS -import transaction -from transaction import Transaction -import paymentrequest -from paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED -import contacts +from .import util +from .util import print_msg, format_satoshis, print_stderr +from .import bitcoin +from .bitcoin import is_address, hash_160, COIN, TYPE_ADDRESS +from .transaction import Transaction +from .import paymentrequest +from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED +from .import contacts +if six.PY3: + long = int known_commands = {} @@ -53,7 +59,6 @@ def satoshis(amount): class Command: - def __init__(self, func, s): self.name = func.__name__ self.requires_network = 'n' in s @@ -61,8 +66,8 @@ class Command: self.requires_password = 'p' in s self.description = func.__doc__ self.help = self.description.split('.')[0] if self.description else None - varnames = func.func_code.co_varnames[1:func.func_code.co_argcount] - self.defaults = func.func_defaults + varnames = func.__code__.co_varnames[1:func.__code__.co_argcount] + self.defaults = func.__defaults__ if self.defaults: n = len(self.defaults) self.params = list(varnames[:-n]) @@ -728,7 +733,7 @@ command_options = { # don't use floats because of rounding errors -from transaction import tx_from_str +from .transaction import tx_from_str json_loads = lambda x: json.loads(x, parse_float=lambda x: str(Decimal(x))) arg_types = { 'num': int, diff --git a/lib/contacts.py b/lib/contacts.py @@ -20,6 +20,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import sys import re @@ -27,10 +33,10 @@ import dns import os import json -import bitcoin -import dnssec -from util import print_error -from i18n import _ +from . import bitcoin +from . import dnssec +from .util import print_error +from .i18n import _ class Contacts(dict): diff --git a/lib/daemon.py b/lib/daemon.py @@ -22,25 +22,30 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import ast import os import sys import time -import jsonrpclib -from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler +# import jsonrpclib +# from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer, SimpleJSONRPCRequestHandler -from version import ELECTRUM_VERSION -from network import Network -from util import json_decode, DaemonThread -from util import print_msg, print_error, print_stderr, UserCancelled -from wallet import Wallet -from storage import WalletStorage -from commands import known_commands, Commands -from simple_config import SimpleConfig -from plugins import run_hook -from exchange_rate import FxThread +from .version import ELECTRUM_VERSION +from .network import Network +from .util import json_decode, DaemonThread +from .util import print_msg, print_error, print_stderr, UserCancelled +from .wallet import Wallet +from .storage import WalletStorage +from .commands import known_commands, Commands +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') @@ -85,18 +90,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): diff --git a/lib/dnssec.py b/lib/dnssec.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six # Check DNSSEC trust chain. @@ -61,25 +66,21 @@ import dns.rdtypes.IN.AAAA from dns.exception import DNSException - -""" -Pure-Python version of dns.dnssec._validate_rsig -""" - +# Pure-Python version of dns.dnssec._validate_rsig import ecdsa -import rsakey +from . import rsakey def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None): from dns.dnssec import ValidationFailure, ECDSAP256SHA256, ECDSAP384SHA384 from dns.dnssec import _find_candidate_keys, _make_hash, _is_ecdsa, _is_rsa, _to_rdata, _make_algorithm_id - if isinstance(origin, (str, unicode)): + if isinstance(origin, six.text_type): origin = dns.name.from_text(origin, dns.name.root) for candidate_key in _find_candidate_keys(keys, rrsig): if not candidate_key: - raise ValidationFailure, 'unknown key' + raise ValidationFailure('unknown key') # For convenience, allow the rrset to be specified as a (name, rdataset) # tuple as well as a proper rrset @@ -93,9 +94,9 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None): if now is None: now = time.time() if rrsig.expiration < now: - raise ValidationFailure, 'expired' + raise ValidationFailure('expired') if rrsig.inception > now: - raise ValidationFailure, 'not yet valid' + raise ValidationFailure('not yet valid') hash = _make_hash(rrsig.algorithm) @@ -124,7 +125,7 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None): digest_len = 48 else: # shouldn't happen - raise ValidationFailure, 'unknown ECDSA curve' + raise ValidationFailure('unknown ECDSA curve') keyptr = candidate_key.key x = ecdsa.util.string_to_number(keyptr[0:key_len]) y = ecdsa.util.string_to_number(keyptr[key_len:key_len * 2]) @@ -137,7 +138,7 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None): ecdsa.util.string_to_number(s)) else: - raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm + raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) hash.update(_to_rdata(rrsig, origin)[:18]) hash.update(rrsig.signer.to_digestable(origin)) @@ -170,9 +171,9 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None): return else: - raise ValidationFailure, 'unknown algorithm %u' % rrsig.algorithm + raise ValidationFailure('unknown algorithm %s' % rrsig.algorithm) - raise ValidationFailure, 'verify failure' + raise ValidationFailure('verify failure') # replace validate_rrsig @@ -182,7 +183,7 @@ dns.dnssec.validate = dns.dnssec._validate -from util import print_error +from .util import print_error # hard-coded trust anchors (root KSKs) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six from datetime import datetime import inspect import requests @@ -8,10 +14,10 @@ import traceback import csv from decimal import Decimal -from bitcoin import COIN -from i18n import _ -from util import PrintError, ThreadJob -from util import format_satoshis +from .bitcoin import COIN +from .i18n import _ +from .util import PrintError, ThreadJob +from .util import format_satoshis # See https://en.wikipedia.org/wiki/ISO_4217 @@ -22,6 +28,7 @@ CCY_PRECISIONS = {'BHD': 3, 'BIF': 0, 'BYR': 0, 'CLF': 4, 'CLP': 0, 'RWF': 0, 'TND': 3, 'UGX': 0, 'UYI': 0, 'VND': 0, 'VUV': 0, 'XAF': 0, 'XAU': 4, 'XOF': 0, 'XPF': 0} + class ExchangeBase(PrintError): def __init__(self, on_quotes, on_history): @@ -323,7 +330,7 @@ class Winkdex(ExchangeBase): def dictinvert(d): inv = {} - for k, vlist in d.iteritems(): + for k, vlist in d.items(): for v in vlist: keys = inv.setdefault(v, []) keys.append(k) diff --git a/lib/i18n.py b/lib/i18n.py @@ -22,16 +22,24 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals -import gettext, os +import gettext, os, six LOCALE_DIR = os.path.join(os.path.dirname(__file__), 'locale') language = gettext.translation('electrum', LOCALE_DIR, fallback = True) - -def _(x): - global language - return language.ugettext(x) +if six.PY2: + def _(x): + global language + return language.ugettext(x) +else: + def _(x): + global language + return language.gettext(x) def set_language(x): global language diff --git a/lib/interface.py b/lib/interface.py @@ -22,8 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals - +import six import os import re import socket @@ -36,9 +40,9 @@ import traceback import requests ca_path = requests.certs.where() -import util -import x509 -import pem +from . import util +from . import x509 +from . import pem def Connection(server, queue, config_path): @@ -80,7 +84,7 @@ class TcpConnection(threading.Thread, util.PrintError): # None/{} is not acceptable. if not peercert: return False - if peercert.has_key("subjectAltName"): + if 'subjectAltName' in peercert: for typ, val in peercert["subjectAltName"]: if typ == "DNS" and val == name: return True @@ -102,6 +106,7 @@ class TcpConnection(threading.Thread, util.PrintError): except socket.gaierror: self.print_error("cannot resolve hostname") return + e = None for res in l: try: s = socket.socket(res[0], socket.SOCK_STREAM) @@ -110,7 +115,8 @@ class TcpConnection(threading.Thread, util.PrintError): s.settimeout(2) s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) return s - except BaseException as e: + except BaseException as _e: + e = _e continue else: self.print_error("failed to connect", str(e)) @@ -126,7 +132,7 @@ class TcpConnection(threading.Thread, util.PrintError): # 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) - except ssl.SSLError, e: + except ssl.SSLError as e: s = None if s and self.check_host_name(s.getpeercert(), self.host): self.print_error("SSL certificate signed by CA") @@ -138,7 +144,7 @@ class TcpConnection(threading.Thread, util.PrintError): return try: s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv23, cert_reqs=ssl.CERT_NONE, ca_certs=None) - except ssl.SSLError, e: + except ssl.SSLError as e: self.print_error("SSL error retrieving SSL certificate:", e) return @@ -164,7 +170,7 @@ class TcpConnection(threading.Thread, util.PrintError): cert_reqs=ssl.CERT_REQUIRED, ca_certs= (temporary_path if is_new else cert_path), do_handshake_on_connect=True) - except ssl.SSLError, e: + except ssl.SSLError as e: self.print_error("SSL error:", e) if e.errno != 1: return @@ -191,7 +197,7 @@ class TcpConnection(threading.Thread, util.PrintError): return self.print_error("wrong certificate") return - except BaseException, e: + except BaseException as e: self.print_error(e) if e.errno == 104: return @@ -264,12 +270,12 @@ class Interface(util.PrintError): def send_requests(self): '''Sends queued requests. Returns False on failure.''' - make_dict = lambda (m, p, i): {'method': m, 'params': p, 'id': i} + make_dict = lambda m, p, i: {'method': m, 'params': p, 'id': i} n = self.num_requests() wire_requests = self.unsent_requests[0:n] try: self.pipe.send_all(map(make_dict, wire_requests)) - except socket.error, e: + except socket.error as e: self.print_error("socket error:", e) return False self.unsent_requests = self.unsent_requests[n:] @@ -363,12 +369,12 @@ def _match_hostname(name, val): return val.startswith('*.') and name.endswith(val[1:]) def test_certificates(): - from simple_config import SimpleConfig + from .simple_config import SimpleConfig config = SimpleConfig() mydir = os.path.join(config.path, "certs") certs = os.listdir(mydir) for c in certs: - print c + print(c) p = os.path.join(mydir,c) with open(p) as f: cert = f.read() diff --git a/lib/keystore.py b/lib/keystore.py @@ -28,15 +28,15 @@ import struct from unicodedata import normalize -from version import * -import bitcoin -from bitcoin import pw_encode, pw_decode, bip32_root, bip32_private_derivation, bip32_public_derivation, bip32_private_key, deserialize_xprv, deserialize_xpub -from bitcoin import public_key_from_private_key, public_key_to_p2pkh -from bitcoin import * +from .version import * +from . import bitcoin +from .bitcoin import pw_encode, pw_decode, bip32_root, bip32_private_derivation, bip32_public_derivation, bip32_private_key, deserialize_xprv, deserialize_xpub +from .bitcoin import public_key_from_private_key, public_key_to_p2pkh +from .bitcoin import * -from bitcoin import is_old_seed, is_new_seed, is_seed -from util import PrintError, InvalidPassword -from mnemonic import Mnemonic, load_wordlist +from .bitcoin import is_old_seed, is_new_seed, is_seed +from .util import PrintError, InvalidPassword +from .mnemonic import Mnemonic, load_wordlist class KeyStore(PrintError): @@ -170,7 +170,7 @@ class Imported_KeyStore(Software_KeyStore): # fixme: this assumes p2pkh _, addr = xpubkey_to_address(x_pubkey) for pubkey in self.keypairs.keys(): - if public_key_to_p2pkh(pubkey.decode('hex')) == addr: + if public_key_to_p2pkh(bfh(pubkey)) == addr: return pubkey def update_password(self, old_password, new_password): @@ -247,22 +247,22 @@ class Xpub: _, _, _, _, c, cK = deserialize_xpub(xpub) for i in sequence: cK, c = CKD_pub(cK, c, i) - return cK.encode('hex') + return bh2u(cK) def get_xpubkey(self, c, i): s = ''.join(map(lambda x: bitcoin.int_to_hex(x,2), (c, i))) - return 'ff' + bitcoin.DecodeBase58Check(self.xpub).encode('hex') + s + return 'ff' + bh2u(bitcoin.DecodeBase58Check(self.xpub)) + s @classmethod def parse_xpubkey(self, pubkey): assert pubkey[0:2] == 'ff' - pk = pubkey.decode('hex') + pk = bfh(pubkey) pk = pk[1:] xkey = bitcoin.EncodeBase58Check(pk[0:78]) dd = pk[78:] s = [] while dd: - n = int(bitcoin.rev_hex(dd[0:2].encode('hex')), 16) + n = int(bitcoin.rev_hex(bh2u(dd[0:2])), 16) dd = dd[2:] s.append(n) assert len(s) == 2 @@ -277,7 +277,6 @@ class Xpub: return derivation - class BIP32_KeyStore(Deterministic_KeyStore, Xpub): def __init__(self, d): @@ -363,12 +362,12 @@ class Old_KeyStore(Deterministic_KeyStore): self.mpk = mpk def format_seed(self, seed): - import old_mnemonic + from . import old_mnemonic # see if seed was entered as hex seed = seed.strip() if seed: try: - seed.decode('hex') + bfh(seed) return str(seed) except Exception: pass @@ -379,7 +378,7 @@ class Old_KeyStore(Deterministic_KeyStore): return seed def get_seed(self, password): - import old_mnemonic + from . import old_mnemonic s = self.get_hex_seed(password) return ' '.join(old_mnemonic.mn_encode(s)) @@ -388,7 +387,7 @@ class Old_KeyStore(Deterministic_KeyStore): secexp = klass.stretch_key(seed) master_private_key = ecdsa.SigningKey.from_secret_exponent(secexp, curve = SECP256k1) master_public_key = master_private_key.get_verifying_key().to_string() - return master_public_key.encode('hex') + return bh2u(master_public_key) @classmethod def stretch_key(self, seed): @@ -399,20 +398,20 @@ class Old_KeyStore(Deterministic_KeyStore): @classmethod def get_sequence(self, mpk, for_change, n): - return string_to_number(Hash("%d:%d:"%(n, for_change) + mpk.decode('hex'))) + return string_to_number(Hash(("%d:%d:"%(n, for_change)).encode('ascii') + bfh(mpk))) def get_address(self, for_change, n): pubkey = self.get_pubkey(for_change, n) - address = public_key_to_p2pkh(pubkey.decode('hex')) + address = public_key_to_p2pkh(bfh(pubkey)) return address @classmethod def get_pubkey_from_mpk(self, mpk, for_change, n): z = self.get_sequence(mpk, for_change, n) - master_public_key = ecdsa.VerifyingKey.from_string(mpk.decode('hex'), curve = SECP256k1) + master_public_key = ecdsa.VerifyingKey.from_string(bfh(mpk), curve = SECP256k1) pubkey_point = master_public_key.pubkey.point + z*SECP256k1.generator public_key2 = ecdsa.VerifyingKey.from_public_point(pubkey_point, curve = SECP256k1) - return '04' + public_key2.to_string().encode('hex') + return '04' + bh2u(public_key2.to_string()) def derive_pubkey(self, for_change, n): return self.get_pubkey_from_mpk(self.mpk, for_change, n) @@ -436,8 +435,8 @@ class Old_KeyStore(Deterministic_KeyStore): secexp = self.stretch_key(seed) master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 ) master_public_key = master_private_key.get_verifying_key().to_string() - if master_public_key != self.mpk.decode('hex'): - print_error('invalid password (mpk)', self.mpk, master_public_key.encode('hex')) + if master_public_key != bfh(self.mpk): + print_error('invalid password (mpk)', self.mpk, bh2u(master_public_key)) raise InvalidPassword() def check_password(self, password): @@ -590,17 +589,20 @@ def bip39_is_checksum_valid(mnemonic): def is_xpubkey(x_pubkey): return x_pubkey[0:2] == 'ff' + def parse_xpubkey(x_pubkey): assert x_pubkey[0:2] == 'ff' return BIP32_KeyStore.parse_xpubkey(x_pubkey) + def xpubkey_to_address(x_pubkey): if x_pubkey[0:2] == 'fd': - addrtype = ord(x_pubkey[2:4].decode('hex')) - hash160 = x_pubkey[4:].decode('hex') + # TODO: check that ord() is OK here + addrtype = ord(bfh(x_pubkey[2:4])) + hash160 = bfh(x_pubkey[4:]) address = bitcoin.hash_160_to_bc_address(hash160, addrtype) return x_pubkey, address - if x_pubkey[0:2] in ['02','03','04']: + if x_pubkey[0:2] in ['02', '03', '04']: pubkey = x_pubkey elif x_pubkey[0:2] == 'ff': xpub, s = BIP32_KeyStore.parse_xpubkey(x_pubkey) @@ -611,7 +613,7 @@ def xpubkey_to_address(x_pubkey): else: raise BaseException("Cannot parse pubkey") if pubkey: - address = public_key_to_p2pkh(pubkey.decode('hex')) + address = public_key_to_p2pkh(bfh(pubkey)) return pubkey, address def xpubkey_to_pubkey(x_pubkey): @@ -649,7 +651,6 @@ def load_keystore(storage, name): return k - def is_old_mpk(mpk): try: int(mpk, 16) @@ -657,10 +658,12 @@ def is_old_mpk(mpk): return False return len(mpk) == 128 + def is_address_list(text): parts = text.split() return bool(parts) and all(bitcoin.is_address(x) for x in parts) + def get_private_keys(text): parts = text.split('\n') parts = map(lambda x: ''.join(x.split()), parts) @@ -668,15 +671,18 @@ def get_private_keys(text): if bool(parts) and all(bitcoin.is_private_key(x) for x in parts): return parts + def is_private_key_list(text): return bool(get_private_keys(text)) + is_mpk = lambda x: is_old_mpk(x) or is_xpub(x) is_private = lambda x: is_seed(x) or is_xprv(x) or is_private_key_list(x) is_any_key = lambda x: is_old_mpk(x) or is_xprv(x) or is_xpub(x) or is_private_key_list(x) is_private_key = lambda x: is_xprv(x) or is_private_key_list(x) is_bip32_key = lambda x: is_xprv(x) or is_xpub(x) + def bip44_derivation(account_id): if bitcoin.TESTNET: return "m/44'/1'/%d'"% int(account_id) diff --git a/lib/mnemonic.py b/lib/mnemonic.py @@ -22,6 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import os import hmac @@ -33,10 +39,10 @@ import string import ecdsa import pbkdf2 -from util import print_error -from bitcoin import is_old_seed, is_new_seed -import version -import i18n +from .util import print_error +from .bitcoin import is_old_seed, is_new_seed +from . import version +from . import i18n # http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/e_asia.html CJK_INTERVALS = [ @@ -80,7 +86,10 @@ def is_CJK(c): def normalize_text(seed): # normalize - seed = unicodedata.normalize('NFKD', unicode(seed)) + if six.PY2: + seed = unicodedata.normalize('NFKD', unicode(seed)) + else: + seed = unicodedata.normalize('NFKD', str(seed)) # lower seed = seed.lower() # remove accents @@ -139,7 +148,7 @@ class Mnemonic(object): words = [] while i: x = i%n - i = i/n + i = i//n words.append(self.wordlist[x]) return ' '.join(words) diff --git a/lib/msqr.py b/lib/msqr.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ def modular_sqrt(a, p): @@ -26,7 +32,7 @@ def modular_sqrt(a, p): elif p == 2: return p elif p % 4 == 3: - return pow(a, (p + 1) / 4, p) + return pow(a, (p + 1) // 4, p) # Partition p-1 to s * 2^e for an odd s (i.e. # reduce all the powers of 2 from p-1) @@ -34,7 +40,7 @@ def modular_sqrt(a, p): s = p - 1 e = 0 while s % 2 == 0: - s /= 2 + s //= 2 e += 1 # Find some 'n' with a legendre symbol n|p = -1. @@ -59,7 +65,7 @@ def modular_sqrt(a, p): # both a and b # r is the exponent - decreases with each update # - x = pow(a, (s + 1) / 2, p) + x = pow(a, (s + 1) // 2, p) b = pow(a, s, p) g = pow(n, s, p) r = e @@ -67,7 +73,7 @@ def modular_sqrt(a, p): while True: t = b m = 0 - for m in xrange(r): + for m in range(r): if t == 1: break t = pow(t, 2, p) @@ -90,5 +96,5 @@ def legendre_symbol(a, p): Returns 1 if a has a square root modulo p, -1 otherwise. """ - ls = pow(a, (p - 1) / 2, p) + ls = pow(a, (p - 1) // 2, p) return -1 if ls == p - 1 else ls diff --git a/lib/network.py b/lib/network.py @@ -20,9 +20,14 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import time -import Queue +from six.moves import queue import os import errno import sys @@ -32,16 +37,16 @@ import traceback from collections import defaultdict, deque import threading -import socks import socket import json -import util -import bitcoin -from bitcoin import * -from interface import Connection, Interface -import blockchain -from version import ELECTRUM_VERSION, PROTOCOL_VERSION +from . import socks +from . import util +from . import bitcoin +from .bitcoin import * +from .interface import Connection, Interface +from . import blockchain +from .version import ELECTRUM_VERSION, PROTOCOL_VERSION DEFAULT_PORTS = {'t':'50001', 's':'50002'} @@ -143,7 +148,7 @@ def pick_random_server(hostmap = None, protocol = 's', exclude_set = set()): eligible = list(set(filter_protocol(hostmap, protocol)) - exclude_set) return random.choice(eligible) if eligible else None -from simple_config import SimpleConfig +from .simple_config import SimpleConfig proxy_modes = ['socks4', 'socks5', 'http'] @@ -255,7 +260,7 @@ class Network(util.DaemonThread): self.interfaces = {} self.auto_connect = self.config.get('auto_connect', True) self.connecting = set() - self.socket_queue = Queue.Queue() + self.socket_queue = queue.Queue() self.start_network(deserialize_server(self.default_server)[2], deserialize_proxy(self.config.get('proxy'))) @@ -436,8 +441,9 @@ class Network(util.DaemonThread): # prevent dns leaks, see http://stackoverflow.com/questions/13184205/dns-over-proxy socket.getaddrinfo = lambda *args: [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))] else: - socket.socket = socket._socketobject - socket.getaddrinfo = socket._socket.getaddrinfo + if six.PY2: + socket.socket = socket._socketobject + socket.getaddrinfo = socket._socket.getaddrinfo def start_network(self, protocol, proxy): assert not self.interface and not self.interfaces @@ -458,7 +464,7 @@ class Network(util.DaemonThread): assert not self.interfaces self.connecting = set() # Get a new queue - no old pending connections thanks! - self.socket_queue = Queue.Queue() + self.socket_queue = queue.Queue() def set_parameters(self, host, port, protocol, proxy, auto_connect): proxy_str = serialize_proxy(proxy) @@ -936,7 +942,9 @@ class Network(util.DaemonThread): win = [i for i in self.interfaces.values() if i.num_requests()] try: rout, wout, xout = select.select(rin, win, [], 0.1) - except socket.error as (code, msg): + except socket.error as e: + # TODO: py3, get code from e + code = None if code == errno.EINTR: return raise @@ -1056,11 +1064,11 @@ class Network(util.DaemonThread): return self.blockchain().height() def synchronous_get(self, request, timeout=30): - queue = Queue.Queue() + queue = queue.Queue() self.send([request], queue.put) try: r = queue.get(True, timeout) - except Queue.Empty: + except queue.Empty: raise BaseException('Server did not answer') if r.get('error'): raise BaseException(r.get('error')) diff --git a/lib/old_mnemonic.py b/lib/old_mnemonic.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six # list of words from http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/Contemporary_poetry @@ -1665,18 +1670,19 @@ n = 1626 def mn_encode( message ): assert len(message) % 8 == 0 out = [] - for i in range(len(message)/8): + for i in range(len(message)//8): word = message[8*i:8*i+8] x = int(word, 16) w1 = (x%n) - w2 = ((x/n) + w1)%n - w3 = ((x/n/n) + w2)%n + w2 = ((x//n) + w1)%n + w3 = ((x//n//n) + w2)%n out += [ words[w1], words[w2], words[w3] ] return out + def mn_decode( wlist ): out = '' - for i in range(len(wlist)/3): + for i in range(len(wlist)//3): word1, word2, word3 = wlist[3*i:3*i+3] w1 = words.index(word1) w2 = (words.index(word2))%n @@ -1688,9 +1694,9 @@ def mn_decode( wlist ): if __name__ == '__main__': import sys - if len( sys.argv ) == 1: - print 'I need arguments: a hex string to encode, or a list of words to decode' - elif len( sys.argv ) == 2: - print ' '.join(mn_encode(sys.argv[1])) + if len(sys.argv) == 1: + print('I need arguments: a hex string to encode, or a list of words to decode') + elif len(sys.argv) == 2: + print(' '.join(mn_encode(sys.argv[1]))) else: - print mn_decode(sys.argv[1:]) + print(mn_decode(sys.argv[1:])) diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py @@ -22,8 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals - +import six import hashlib import os.path import re @@ -31,23 +35,28 @@ import sys import threading import time import traceback -import urlparse import json import requests +from six.moves import urllib_parse + + try: - import paymentrequest_pb2 as pb2 + if six.PY3: + from . import paymentrequest_pb2_py3 as pb2 + else: + from . import paymentrequest_pb2 as pb2 except ImportError: sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=lib/ --python_out=lib/ lib/paymentrequest.proto'") -import bitcoin -import util -from util import print_error -import transaction -import x509 -import rsakey +from . import bitcoin +from . import util +from .util import print_error +from . import transaction +from . import x509 +from . import rsakey -from bitcoin import TYPE_ADDRESS +from .bitcoin import TYPE_ADDRESS REQUEST_HEADERS = {'Accept': 'application/bitcoin-paymentrequest', 'User-Agent': 'Electrum'} ACK_HEADERS = {'Content-Type':'application/bitcoin-payment','Accept':'application/bitcoin-paymentack','User-Agent':'Electrum'} @@ -72,7 +81,7 @@ PR_PAID = 3 # send and propagated def get_payment_request(url): - u = urlparse.urlparse(url) + u = urllib_parse.urlparse(url) error = None if u.scheme in ['http', 'https']: try: @@ -275,15 +284,15 @@ class PaymentRequest: paymnt.memo = "Paid using Electrum" pm = paymnt.SerializeToString() - payurl = urlparse.urlparse(pay_det.payment_url) + payurl = urllib_parse.urlparse(pay_det.payment_url) try: r = requests.post(payurl.geturl(), data=pm, headers=ACK_HEADERS, verify=ca_path) except requests.exceptions.SSLError: - print "Payment Message/PaymentACK verify Failed" + print("Payment Message/PaymentACK verify Failed") try: r = requests.post(payurl.geturl(), data=pm, headers=ACK_HEADERS, verify=False) except Exception as e: - print e + print(e) return False, "Payment Message/PaymentACK Failed" if r.status_code >= 500: @@ -295,12 +304,12 @@ class PaymentRequest: except Exception: return False, "PaymentACK could not be processed. Payment was sent; please manually verify that payment was received." - print "PaymentACK message received: %s" % paymntack.memo + print("PaymentACK message received: %s" % paymntack.memo) return True, paymntack.memo def make_unsigned_request(req): - from transaction import Transaction + from .transaction import Transaction addr = req['address'] time = req.get('time', 0) exp = req.get('exp', 0) @@ -392,7 +401,7 @@ def verify_cert_chain(chain): def check_ssl_config(config): - import pem + from . import pem key_path = config.get('ssl_privkey') cert_path = config.get('ssl_chain') with open(key_path, 'r') as f: @@ -414,7 +423,7 @@ def check_ssl_config(config): return requestor def sign_request_with_x509(pr, key_path, cert_path): - import pem + from . import pem with open(key_path, 'r') as f: params = pem.parse_private_key(f.read()) privkey = rsakey.RSAKey(*params) diff --git a/lib/pem.py b/lib/pem.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six # This module uses code from TLSLlite # TLSLite Author: Trevor Perrin) @@ -30,7 +35,7 @@ import binascii -from x509 import ASN1_Node, bytestr_to_int, decode_OID +from .x509 import ASN1_Node, bytestr_to_int, decode_OID def a2b_base64(s): diff --git a/lib/plugins.py b/lib/plugins.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six from collections import namedtuple import traceback import sys @@ -30,10 +35,11 @@ import os import imp import pkgutil import time +import threading -from util import * -from i18n import _ -from util import profiler, PrintError, DaemonThread, UserCancelled +from .util import * +from .i18n import _ +from .util import profiler, PrintError, DaemonThread, UserCancelled, ThreadJob plugin_loaders = {} hook_names = set() @@ -156,7 +162,7 @@ class Plugins(DaemonThread): return out def register_wallet_type(self, name, gui_good, wallet_type): - from wallet import register_wallet_type, register_constructor + from .wallet import register_wallet_type, register_constructor self.print_error("registering wallet type", (wallet_type, name)) def loader(): plugin = self.get_plugin(name) @@ -165,7 +171,7 @@ class Plugins(DaemonThread): plugin_loaders[wallet_type] = loader def register_keystore(self, name, gui_good, details): - from keystore import register_keystore + from .keystore import register_keystore def dynamic_constructor(d): return self.get_plugin(name).keystore_class(d) if details[0] == 'hardware': @@ -299,7 +305,7 @@ class DeviceMgr(ThreadJob, PrintError): def __init__(self, config): super(DeviceMgr, self).__init__() - # Keyed by xpub. The value is the device id + # Keyed by xpub. The value is the device id # has been paired, and None otherwise. self.xpub_ids = {} # A list of clients. The key is the client, the value is diff --git a/lib/rsakey.py b/lib/rsakey.py @@ -33,15 +33,19 @@ """Pure-Python RSA implementation.""" - +from __future__ import absolute_import +from __future__ import division from __future__ import print_function +from __future__ import unicode_literals + +import six import os import math import base64 import binascii import hashlib -from pem import * +from .pem import * def SHA1(x): diff --git a/lib/simple_config.py b/lib/simple_config.py @@ -1,12 +1,18 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import ast import json import threading import os from copy import deepcopy -from util import user_dir, print_error, print_msg, print_stderr, PrintError +from .util import user_dir, print_error, print_msg, print_stderr, PrintError -from bitcoin import MAX_FEE_RATE, FEE_TARGETS +from .bitcoin import MAX_FEE_RATE, FEE_TARGETS SYSTEM_CONFIG_PATH = "/etc/electrum.conf" @@ -96,7 +102,7 @@ class SimpleConfig(PrintError): def fixup_config_keys(self, config, keypairs): updated = False - for old_key, new_key in keypairs.iteritems(): + for old_key, new_key in keypairs.items(): if old_key in config: if not new_key in config: config[new_key] = config[old_key] @@ -252,17 +258,18 @@ def read_system_config(path=SYSTEM_CONFIG_PATH): result = {} if os.path.exists(path): try: - import ConfigParser + from six.moves import configparser + # import ConfigParser except ImportError: - print "cannot parse electrum.conf. please install ConfigParser" + print("cannot parse electrum.conf. please install ConfigParser") return - p = ConfigParser.ConfigParser() + p = configparser.ConfigParser() try: p.read(path) for k, v in p.items('client'): result[k] = v - except (ConfigParser.NoSectionError, ConfigParser.MissingSectionHeaderError): + except (configparser.NoSectionError, configparser.MissingSectionHeaderError): pass return result diff --git a/lib/storage.py b/lib/storage.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import six import os import ast import threading @@ -36,11 +41,11 @@ import pbkdf2, hmac, hashlib import base64 import zlib -from i18n import _ -from util import NotEnoughFunds, PrintError, profiler -from plugins import run_hook, plugin_loaders -from keystore import bip44_derivation -import bitcoin +from .i18n import _ +from .util import NotEnoughFunds, PrintError, profiler +from .plugins import run_hook, plugin_loaders +from .keystore import bip44_derivation +import .bitcoin # seed_version is now used for the version of the wallet file diff --git a/lib/synchronizer.py b/lib/synchronizer.py @@ -22,14 +22,18 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals - +import six from threading import Lock import hashlib -from bitcoin import Hash, hash_encode -from transaction import Transaction -from util import print_error, print_msg, ThreadJob +from .bitcoin import Hash, hash_encode +from .transaction import Transaction +from .util import print_error, print_msg, ThreadJob class Synchronizer(ThreadJob): diff --git a/lib/tests/test_account.py b/lib/tests/test_account.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest class Test_Account(unittest.TestCase): diff --git a/lib/tests/test_bitcoin.py b/lib/tests/test_bitcoin.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest import sys from ecdsa.util import number_to_string @@ -8,6 +14,7 @@ from lib.bitcoin import ( pw_decode, Hash, public_key_from_private_key, address_from_private_key, is_valid, is_private_key, xpub_from_xprv, is_new_seed, is_old_seed, var_int, op_push) +from lib.util import bfh try: import ecdsa @@ -18,7 +25,7 @@ except ImportError: class Test_bitcoin(unittest.TestCase): def test_crypto(self): - for message in ["Chancellor on brink of second bailout for banks", chr(255)*512]: + for message in [b"Chancellor on brink of second bailout for banks", b'\xff'*512]: self._do_test_crypto(message) def _do_test_crypto(self, message): @@ -60,7 +67,7 @@ class Test_bitcoin(unittest.TestCase): assert xprv == "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j" def _do_test_bip32(self, seed, sequence): - xprv, xpub = bip32_root(seed.decode('hex'), 0) + xprv, xpub = bip32_root(bfh(seed), 0) assert sequence[0:2] == "m/" path = 'm' sequence = sequence[2:] @@ -106,7 +113,7 @@ class Test_bitcoin(unittest.TestCase): def test_hash(self): """Make sure the Hash function does sha256 twice""" payload = u"test" - expected = '\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4' + expected = b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4' result = Hash(payload) self.assertEqual(expected, result) diff --git a/lib/tests/test_interface.py b/lib/tests/test_interface.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest from lib import interface diff --git a/lib/tests/test_mnemonic.py b/lib/tests/test_mnemonic.py @@ -1,13 +1,21 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest from lib import keystore from lib import mnemonic from lib import old_mnemonic +from lib.util import bh2u + class Test_NewMnemonic(unittest.TestCase): def test_to_seed(self): seed = mnemonic.Mnemonic.mnemonic_to_seed(mnemonic='foobar', passphrase='none') - self.assertEquals(seed.encode('hex'), + self.assertEquals(bh2u(seed), '741b72fd15effece6bfe5a26a52184f66811bd2be363190e07a42cca442b1a5b' 'b22b3ad0eb338197287e6d314866c7fba863ac65d3f156087a5052ebc7157fce') diff --git a/lib/tests/test_simple_config.py b/lib/tests/test_simple_config.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import ast import sys import os @@ -6,7 +12,7 @@ import tempfile import shutil import json -from StringIO import StringIO +from six.moves import StringIO from lib.simple_config import (SimpleConfig, read_system_config, read_user_config) @@ -230,15 +236,6 @@ class TestUserConfig(unittest.TestCase): result = read_user_config(None) self.assertEqual({}, result) - def test_path_with_reprd_dict(self): - thefile = os.path.join(self.user_dir, "config") - payload = {"gap_limit": 5} - with open(thefile, "w") as f: - f.write(json.dumps(payload)) - - result = read_user_config(self.user_dir) - self.assertEqual(payload, result) - def test_path_without_config_file(self): """We pass a path but if does not contain a "config" file.""" result = read_user_config(self.user_dir) diff --git a/lib/tests/test_transaction.py b/lib/tests/test_transaction.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest from lib import transaction from lib.bitcoin import TYPE_ADDRESS @@ -5,6 +11,10 @@ from lib.bitcoin import TYPE_ADDRESS import pprint from lib.keystore import xpubkey_to_address +from lib.util import bh2u + +from lib.util import bh2u + unsigned_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000005701ff4c53ff0488b21e03ef2afea18000000089689bff23e1e7fb2f161daa37270a97a3d8c2e537584b2d304ecb47b86d21fc021b010d3bd425f8cf2e04824bfdf1f1f5ff1d51fadd9a41f9e3fb8dd3403b1bfe00000000ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000' signed_blob = '01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65bf38633b424eb4031000000006c493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6ffffffff0140420f00000000001976a914230ac37834073a42146f11ef8414ae929feaafc388ac00000000' v2_blob = "0200000001191601a44a81e061502b7bfbc6eaa1cef6d1e6af5308ef96c9342f71dbf4b9b5000000006b483045022100a6d44d0a651790a477e75334adfb8aae94d6612d01187b2c02526e340a7fd6c8022028bdf7a64a54906b13b145cd5dab21a26bd4b85d6044e9b97bceab5be44c2a9201210253e8e0254b0c95776786e40984c1aa32a7d03efa6bdacdea5f421b774917d346feffffff026b20fa04000000001976a914024db2e87dd7cfd0e5f266c5f212e21a31d805a588aca0860100000000001976a91421919b94ae5cefcdf0271191459157cdb41c4cbf88aca6240700" @@ -20,7 +30,7 @@ class TestBCDataStream(unittest.TestCase): with self.assertRaises(transaction.SerializationError): s.write_compact_size(-1) - self.assertEquals(s.input.encode('hex'), + self.assertEquals(bh2u(s.input), '0001fcfdfd00fdfffffe00000100feffffffffff0000000001000000ffffffffffffffffff') for v in values: self.assertEquals(s.read_compact_size(), v) @@ -44,11 +54,11 @@ class TestBCDataStream(unittest.TestCase): def test_bytes(self): s = transaction.BCDataStream() - s.write('foobar') - self.assertEquals(s.read_bytes(3), 'foo') - self.assertEquals(s.read_bytes(2), 'ba') - self.assertEquals(s.read_bytes(4), 'r') - self.assertEquals(s.read_bytes(1), '') + s.write(b'foobar') + self.assertEquals(s.read_bytes(3), b'foo') + self.assertEquals(s.read_bytes(2), b'ba') + self.assertEquals(s.read_bytes(4), b'r') + self.assertEquals(s.read_bytes(1), b'') class TestTransaction(unittest.TestCase): diff --git a/lib/tests/test_util.py b/lib/tests/test_util.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import unittest from lib.util import format_satoshis, parse_URI diff --git a/lib/tests/test_wallet.py b/lib/tests/test_wallet.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six import shutil import tempfile import sys @@ -5,8 +11,8 @@ import unittest import os import json -from StringIO import StringIO -from electrum.storage import WalletStorage, FINAL_SEED_VERSION +from six.moves import StringIO +from lib.storage import WalletStorage, FINAL_SEED_VERSION class FakeSynchronizer(object): diff --git a/lib/transaction.py b/lib/transaction.py @@ -27,11 +27,12 @@ # Note: The deserialization code originally comes from ABE. +from . import bitcoin +from .bitcoin import * +from .util import print_error, profiler, to_string -import bitcoin -from bitcoin import * -from bitcoin import hash160_to_p2sh, hash160_to_p2pkh -from util import print_error, profiler +from . import bitcoin +from .bitcoin import * import time import sys import struct @@ -40,9 +41,9 @@ import struct # Workalike python implementation of Bitcoin's CDataStream class. # import struct -import StringIO +from six import StringIO import random -from keystore import xpubkey_to_address, xpubkey_to_pubkey +from .keystore import xpubkey_to_address, xpubkey_to_pubkey NO_SIGNATURE = 'ff' @@ -50,6 +51,7 @@ NO_SIGNATURE = 'ff' class SerializationError(Exception): """ Thrown when there's a problem deserializing or serializing """ + class BCDataStream(object): def __init__(self): self.input = None @@ -59,13 +61,13 @@ class BCDataStream(object): self.input = None self.read_cursor = 0 - def write(self, bytes): # Initialize with string of bytes + def write(self, _bytes): # Initialize with string of _bytes if self.input is None: - self.input = bytes + self.input = bytearray(_bytes) else: - self.input += bytes + self.input += bytearray(_bytes) - def read_string(self): + def read_string(self, encoding='ascii'): # Strings are encoded depending on length: # 0 to 252 : 1-byte-length followed by bytes (if any) # 253 to 65,535 : byte'253' 2-byte-length followed by bytes @@ -81,9 +83,10 @@ class BCDataStream(object): except IndexError: raise SerializationError("attempt to read past end of buffer") - return self.read_bytes(length) + return self.read_bytes(length).decode(encoding) - def write_string(self, string): + def write_string(self, string, encoding='ascii'): + string = to_bytes(string, encoding) # Length-encoded as with read-string self.write_compact_size(len(string)) self.write(string) @@ -115,7 +118,7 @@ class BCDataStream(object): def write_uint64(self, val): return self._write_num('<Q', val) def read_compact_size(self): - size = ord(self.input[self.read_cursor]) + size = self.input[self.read_cursor] self.read_cursor += 1 if size == 253: size = self._read_num('<H') @@ -129,15 +132,15 @@ class BCDataStream(object): if size < 0: raise SerializationError("attempt to write size < 0") elif size < 253: - self.write(chr(size)) + self.write(bytes([size])) elif size < 2**16: - self.write('\xfd') + self.write(b'\xfd') self._write_num('<H', size) elif size < 2**32: - self.write('\xfe') + self.write(b'\xfe') self._write_num('<I', size) elif size < 2**64: - self.write('\xff') + self.write(b'\xff') self._write_num('<Q', size) def _read_num(self, format): @@ -149,15 +152,13 @@ class BCDataStream(object): s = struct.pack(format, num) self.write(s) -# + # enum-like type # From the Python Cookbook, downloaded from http://code.activestate.com/recipes/67107/ -# -import types, string, exceptions - -class EnumException(exceptions.Exception): +class EnumException(Exception): pass + class Enumeration: def __init__(self, name, enumList): self.__doc__ = name @@ -167,16 +168,16 @@ class Enumeration: uniqueNames = [ ] uniqueValues = [ ] for x in enumList: - if type(x) == types.TupleType: + if isinstance(x, tuple): x, i = x - if type(x) != types.StringType: - raise EnumException, "enum name is not a string: " + x - if type(i) != types.IntType: - raise EnumException, "enum value is not an integer: " + i + if not isinstance(x, six.text_type): + raise EnumException("enum name is not a string: " + x) + if not isinstance(i, six.integer_types): + raise EnumException("enum value is not an integer: " + i) if x in uniqueNames: - raise EnumException, "enum name is not unique: " + x + raise EnumException("enum name is not unique: " + x) if i in uniqueValues: - raise EnumException, "enum value is not unique for " + x + raise EnumException("enum value is not unique for " + x) uniqueNames.append(x) uniqueValues.append(i) lookup[x] = i @@ -184,8 +185,9 @@ class Enumeration: i = i + 1 self.lookup = lookup self.reverseLookup = reverseLookup + def __getattr__(self, attr): - if not self.lookup.has_key(attr): + if attr not in self.lookup: raise AttributeError return self.lookup[attr] def whatis(self, value): @@ -228,32 +230,32 @@ opcodes = Enumeration("Opcodes", [ ]) -def script_GetOp(bytes): +def script_GetOp(_bytes): i = 0 - while i < len(bytes): + while i < len(_bytes): vch = None - opcode = ord(bytes[i]) + opcode = _bytes[i] i += 1 if opcode >= opcodes.OP_SINGLEBYTE_END: opcode <<= 8 - opcode |= ord(bytes[i]) + opcode |= _bytes[i] i += 1 if opcode <= opcodes.OP_PUSHDATA4: nSize = opcode if opcode == opcodes.OP_PUSHDATA1: - nSize = ord(bytes[i]) + nSize = _bytes[i] i += 1 elif opcode == opcodes.OP_PUSHDATA2: - (nSize,) = struct.unpack_from('<H', bytes, i) + (nSize,) = struct.unpack_from('<H', _bytes, i) i += 2 elif opcode == opcodes.OP_PUSHDATA4: - (nSize,) = struct.unpack_from('<I', bytes, i) + (nSize,) = struct.unpack_from('<I', _bytes, i) i += 4 - vch = bytes[i:i+nSize] + vch = _bytes[i:i + nSize] i += nSize - yield (opcode, vch, i) + yield opcode, vch, i def script_GetOpName(opcode): @@ -292,21 +294,20 @@ def safe_parse_pubkey(x): except: return x - -def parse_scriptSig(d, bytes): +def parse_scriptSig(d, _bytes): try: - decoded = [ x for x in script_GetOp(bytes) ] - except Exception: + decoded = [ x for x in script_GetOp(_bytes) ] + except Exception as e: # coinbase transactions raise an exception - print_error("cannot find address in input script", bytes.encode('hex')) + print_error("cannot find address in input script", bh2u(_bytes)) return match = [ opcodes.OP_PUSHDATA4 ] if match_decoded(decoded, match): item = decoded[0][1] - if item[0] == chr(0): - redeemScript = item.encode('hex') - d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(redeemScript.decode('hex'))) + if item[0] == 0: + redeemScript = bh2u(item) + d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(item)) d['type'] = 'p2wpkh-p2sh' d['redeemScript'] = redeemScript d['x_pubkeys'] = ["(witness)"] @@ -317,7 +318,7 @@ def parse_scriptSig(d, bytes): # payto_pubkey d['type'] = 'p2pk' d['address'] = "(pubkey)" - d['signatures'] = [item.encode('hex')] + d['signatures'] = [bh2u(item)] d['num_sig'] = 1 d['x_pubkeys'] = ["(pubkey)"] d['pubkeys'] = ["(pubkey)"] @@ -328,13 +329,13 @@ def parse_scriptSig(d, bytes): # (65 bytes) onto the stack: match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ] if match_decoded(decoded, match): - sig = decoded[0][1].encode('hex') - x_pubkey = decoded[1][1].encode('hex') + sig = bh2u(decoded[0][1]) + x_pubkey = bh2u(decoded[1][1]) try: signatures = parse_sig([sig]) pubkey, address = xpubkey_to_address(x_pubkey) - except BaseException: - print_error("cannot find address in input script", bytes.encode('hex')) + except: + print_error("cannot find address in input script", bh2u(_bytes)) return d['type'] = 'p2pkh' d['signatures'] = signatures @@ -347,9 +348,9 @@ def parse_scriptSig(d, bytes): # p2sh transaction, m of n match = [ opcodes.OP_0 ] + [ opcodes.OP_PUSHDATA4 ] * (len(decoded) - 1) if not match_decoded(decoded, match): - print_error("cannot find address in input script", bytes.encode('hex')) + print_error("cannot find address in input script", bh2u(_bytes)) return - x_sig = [x[1].encode('hex') for x in decoded[1:-1]] + x_sig = [bh2u(x[1]) for x in decoded[1:-1]] dec2 = [ x for x in script_GetOp(decoded[-1][1]) ] m = dec2[0][0] - opcodes.OP_1 + 1 n = dec2[-2][0] - opcodes.OP_1 + 1 @@ -357,9 +358,9 @@ def parse_scriptSig(d, bytes): op_n = opcodes.OP_1 + n - 1 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", bytes.encode('hex')) + print_error("cannot find address in input script", bh2u(_bytes)) return - x_pubkeys = map(lambda x: x[1].encode('hex'), dec2[1:-2]) + 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) # write result in d @@ -369,19 +370,17 @@ def parse_scriptSig(d, bytes): d['x_pubkeys'] = x_pubkeys d['pubkeys'] = pubkeys d['redeemScript'] = redeemScript - d['address'] = hash160_to_p2sh(hash_160(redeemScript.decode('hex'))) - + d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript))) - -def get_address_from_output_script(bytes): - decoded = [ x for x in script_GetOp(bytes) ] +def get_address_from_output_script(_bytes): + decoded = [x for x in script_GetOp(_bytes)] # The Genesis Block, self-payments, and pay-by-IP-address payments look like: # 65 BYTES:... CHECKSIG match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ] if match_decoded(decoded, match): - return TYPE_PUBKEY, decoded[0][1].encode('hex') + return TYPE_PUBKEY, bh2u(decoded[0][1]) # Pay-by-Bitcoin-address TxOuts look like: # DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG @@ -394,10 +393,7 @@ def get_address_from_output_script(bytes): if match_decoded(decoded, match): return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1]) - return TYPE_SCRIPT, bytes - - - + return TYPE_SCRIPT, _bytes def parse_input(vds): @@ -406,7 +402,7 @@ def parse_input(vds): prevout_n = vds.read_uint32() scriptSig = vds.read_bytes(vds.read_compact_size()) sequence = vds.read_uint32() - d['scriptSig'] = scriptSig.encode('hex') + d['scriptSig'] = bh2u(scriptSig) d['prevout_hash'] = prevout_hash d['prevout_n'] = prevout_n d['sequence'] = sequence @@ -433,14 +429,14 @@ def parse_output(vds, i): d['value'] = vds.read_int64() scriptPubKey = vds.read_bytes(vds.read_compact_size()) d['type'], d['address'] = get_address_from_output_script(scriptPubKey) - d['scriptPubKey'] = scriptPubKey.encode('hex') + d['scriptPubKey'] = bh2u(scriptPubKey) d['prevout_n'] = i return d def deserialize(raw): vds = BCDataStream() - vds.write(raw.decode('hex')) + vds.write(bfh(raw)) d = {} start = vds.read_cursor d['version'] = vds.read_int32() @@ -448,13 +444,13 @@ def deserialize(raw): is_segwit = (n_vin == 0) if is_segwit: marker = vds.read_bytes(1) - assert marker == chr(1) + assert marker == 1 n_vin = vds.read_compact_size() - d['inputs'] = list(parse_input(vds) for i in xrange(n_vin)) + d['inputs'] = [parse_input(vds) for i in range(n_vin)] n_vout = vds.read_compact_size() - d['outputs'] = list(parse_output(vds,i) for i in xrange(n_vout)) + d['outputs'] = [parse_output(vds,i) for i in range(n_vout)] if is_segwit: - d['witness'] = list(parse_witness(vds) for i in xrange(n_vin)) + d['witness'] = [parse_witness(vds) for i in range(n_vin)] d['lockTime'] = vds.read_uint32() return d @@ -462,27 +458,30 @@ def deserialize(raw): # pay & redeem scripts def push_script(x): - return op_push(len(x)/2) + x + return op_push(len(x)//2) + x + def get_scriptPubKey(addr): addrtype, hash_160 = bc_address_to_hash_160(addr) if addrtype == bitcoin.ADDRTYPE_P2PKH: script = '76a9' # op_dup, op_hash_160 - script += push_script(hash_160.encode('hex')) + script += push_script(bh2u(hash_160)) script += '88ac' # op_equalverify, op_checksig elif addrtype == bitcoin.ADDRTYPE_P2SH: script = 'a9' # op_hash_160 - script += push_script(hash_160.encode('hex')) + script += push_script(bh2u(hash_160)) script += '87' # op_equal else: raise BaseException('unknown address type') return script + def segwit_script(pubkey): pubkey = safe_parse_pubkey(pubkey) - pkh = hash_160(pubkey.decode('hex')).encode('hex') + pkh = bh2u(hash_160(bfh(pubkey))) return '00' + push_script(pkh) + def multisig_script(public_keys, m): n = len(public_keys) assert n <= 15 @@ -505,9 +504,9 @@ class Transaction: def __init__(self, raw): if raw is None: self.raw = None - elif type(raw) in [str, unicode]: + elif isinstance(raw, str): self.raw = raw.strip() if raw else None - elif type(raw) is dict: + elif isinstance(raw, dict): self.raw = raw['hex'] else: raise BaseException("cannot initialize transaction", raw) @@ -553,15 +552,15 @@ class Transaction: for sig in sigs2: if sig in sigs1: continue - pre_hash = Hash(self.serialize_preimage(i).decode('hex')) + pre_hash = Hash(bfh(self.serialize_preimage(i))) # der to string order = ecdsa.ecdsa.generator_secp256k1.order() - r, s = ecdsa.util.sigdecode_der(sig.decode('hex')[:-1], order) + r, s = ecdsa.util.sigdecode_der(bfh(sig[:-2]), order) sig_string = ecdsa.util.sigencode_string(r, s, order) compressed = True for recid in range(4): public_key = MyVerifyingKey.from_signature(sig_string, recid, pre_hash, curve = SECP256k1) - pubkey = point_to_ser(public_key.pubkey.point, compressed).encode('hex') + pubkey = bh2u(point_to_ser(public_key.pubkey.point, compressed)) if pubkey in pubkeys: public_key.verify_digest(sig_string, pre_hash, sigdecode = ecdsa.util.sigdecode_string) j = pubkeys.index(pubkey) @@ -572,7 +571,6 @@ class Transaction: # redo raw self.raw = self.serialize() - def deserialize(self): if self.raw is None: return @@ -597,7 +595,7 @@ class Transaction: @classmethod def pay_script(self, output_type, addr): if output_type == TYPE_SCRIPT: - return addr.encode('hex') + return bh2u(addr) elif output_type == TYPE_ADDRESS: return get_scriptPubKey(addr) else: @@ -616,7 +614,7 @@ class Transaction: else: pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) x_signatures = txin['signatures'] - signatures = filter(None, x_signatures) + signatures = list(filter(None, x_signatures)) is_complete = len(signatures) == num_sig if is_complete: pk_list = pubkeys @@ -671,21 +669,21 @@ class Transaction: return multisig_script(pubkeys, txin['num_sig']) elif txin['type'] == 'p2wpkh-p2sh': pubkey = txin['pubkeys'][0] - pkh = bitcoin.hash_160(pubkey.decode('hex')).encode('hex') + pkh = bh2u(bitcoin.hash_160(bfh(pubkey))) return '76a9' + push_script(pkh) + '88ac' else: raise TypeError('Unknown txin type', _type) @classmethod def serialize_outpoint(self, txin): - return txin['prevout_hash'].decode('hex')[::-1].encode('hex') + int_to_hex(txin['prevout_n'], 4) + return bh2u(bfh(txin['prevout_hash'])[::-1]) + int_to_hex(txin['prevout_n'], 4) @classmethod def serialize_input(self, txin, script): # Prev hash and index s = self.serialize_outpoint(txin) # Script length, script, sequence - s += var_int(len(script)/2) + s += var_int(len(script)//2) s += script s += int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) return s @@ -704,7 +702,7 @@ class Transaction: output_type, addr, amount = output s = int_to_hex(amount, 8) script = self.pay_script(output_type, addr) - s += var_int(len(script)/2) + s += var_int(len(script)//2) s += script return s @@ -715,6 +713,7 @@ class Transaction: inputs = self.inputs() outputs = self.outputs() 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') @@ -750,7 +749,7 @@ class Transaction: return nVersion + txins + txouts + nLocktime def hash(self): - print "warning: deprecated tx.hash()" + print("warning: deprecated tx.hash()") return self.txid() def txid(self): @@ -758,11 +757,11 @@ class Transaction: if not all_segwit and not self.is_complete(): return None ser = self.serialize(witness=False) - return Hash(ser.decode('hex'))[::-1].encode('hex') + return bh2u(Hash(bfh(ser))[::-1]) def wtxid(self): ser = self.serialize(witness=True) - return Hash(ser.decode('hex'))[::-1].encode('hex') + return bh2u(Hash(bfh(ser))[::-1]) def add_inputs(self, inputs): self._inputs.extend(inputs) @@ -787,13 +786,13 @@ class Transaction: @profiler def estimated_size(self): '''Return an estimated tx size in bytes.''' - return len(self.serialize(True)) / 2 if not self.is_complete() or self.raw is None else len(self.raw) / 2 # ASCII hex string + return len(self.serialize(True)) // 2 if not self.is_complete() or self.raw is None else len(self.raw) / 2 # ASCII hex string @classmethod def estimated_input_size(self, txin): '''Return an estimated of serialized input size in bytes.''' script = self.input_script(txin, True) - return len(self.serialize_input(txin, script)) / 2 + return len(self.serialize_input(txin, script)) // 2 def signature_count(self): r = 0 @@ -801,7 +800,7 @@ class Transaction: for txin in self.inputs(): if txin['type'] == 'coinbase': continue - signatures = filter(None, txin.get('signatures',[])) + signatures = list(filter(None, txin.get('signatures',[]))) s += len(signatures) r += txin.get('num_sig',-1) return s, r @@ -824,14 +823,14 @@ class Transaction: sec = keypairs.get(x_pubkey) pubkey = public_key_from_private_key(sec) # add signature - pre_hash = Hash(self.serialize_preimage(i).decode('hex')) + pre_hash = Hash(bfh(self.serialize_preimage(i))) pkey = regenerate_key(sec) secexp = pkey.secret private_key = bitcoin.MySigningKey.from_secret_exponent(secexp, curve = SECP256k1) public_key = private_key.get_verifying_key() sig = private_key.sign_digest_deterministic(pre_hash, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der) assert public_key.verify_digest(sig, pre_hash, sigdecode = ecdsa.util.sigdecode_der) - txin['signatures'][j] = sig.encode('hex') + '01' + txin['signatures'][j] = bh2u(sig) + '01' txin['x_pubkeys'][j] = pubkey txin['pubkeys'][j] = pubkey # needed for fd keys self._inputs[i] = txin @@ -845,9 +844,9 @@ class Transaction: if type == TYPE_ADDRESS: addr = x elif type == TYPE_PUBKEY: - addr = bitcoin.public_key_to_p2pkh(x.decode('hex')) + addr = bitcoin.public_key_to_p2pkh(bfh(x)) else: - addr = 'SCRIPT ' + x.encode('hex') + addr = 'SCRIPT ' + bh2u(x) o.append((addr,v)) # consider using yield (addr, v) return o @@ -869,7 +868,6 @@ class Transaction: } return out - def requires_fee(self, wallet): # see https://en.bitcoin.it/wiki/Transaction_fees # @@ -899,7 +897,7 @@ def tx_from_str(txt): import json txt = txt.strip() try: - txt.decode('hex') + bfh(txt) is_hex = True except: is_hex = False diff --git a/lib/util.py b/lib/util.py @@ -22,7 +22,12 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +import binascii import os, sys, re, json import platform import shutil @@ -30,10 +35,18 @@ from collections import defaultdict from datetime import datetime from decimal import Decimal import traceback -import urlparse import urllib import threading -from i18n import _ +from .i18n import _ + +import six +from six.moves import queue, urllib_parse + +try: + import urllib.parse + import urllib.request, urllib.parse, urllib.error +except ImportError: + pass base_units = {'BTC':8, 'mBTC':5, 'uBTC':2} fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')] @@ -194,7 +207,7 @@ def json_decode(x): # decorator that prints execution time def profiler(func): def do_profile(func, args, kw_args): - n = func.func_name + n = func.__name__ t0 = time.time() o = func(*args, **kw_args) t = time.time() - t0 @@ -238,6 +251,182 @@ def android_check_data_dir(): def get_headers_dir(config): return android_headers_dir() if 'ANDROID_DATA' in os.environ else config.path + +def assert_bytes(*args): + """ + porting helper, assert args type + """ + if six.PY2: + for x in args: + assert isinstance(x, (bytes, bytearray, str)) + return + + try: + if six.PY3: + for x in args: + assert isinstance(x, (bytes, bytearray)) + else: + for x in args: + assert isinstance(x, bytearray) + except: + print('assert bytes failed', list(map(type, args))) + raise + + +def assert_str(*args): + """ + porting helper, assert args type + """ + for x in 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): + if isinstance(x, (bytes, bytearray)): + return x.decode(enc) + if isinstance(x, str): + return x + else: + raise TypeError("Not a string or bytes like object") + +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)) + + +# def ufh(x): +# """ +# py2-py3 aware wrapper for str.decode('hex') +# :param x: str +# :return: str +# """ +# if +# return binascii.unhexlify(x) + + +def hfu(x): + """ + py2-py3 aware wrapper for str.encode('hex') + :param x: str + :return: str + """ + if six.PY3: + assert_bytes(x) + return binascii.hexlify(x) + else: + return x.encode('hex') + + +def bfh(x): + """ + py2-py3 aware wrapper to "bytes.fromhex()" func + :param x: str + :rtype: bytes + """ + if isinstance(x, six.string_types): + return bfh_builder(x) + # TODO: check for iterator interface + elif isinstance(x, (list, tuple, map)): + return [bfh(sub) for sub in x] + else: + raise TypeError('Unexpected type: ' + type(x)) + + +def bh2u(x): + """ + unicode with hex representation of bytes() + e.g. x = bytes([1, 2, 10]) + bh2u(x) -> '01020A' + :param x: bytes + :rtype: str + """ + if six.PY3: + assert_bytes(x) + # x = to_bytes(x, 'ascii') + return binascii.hexlify(x).decode('ascii') + else: + if isinstance(x, bytearray): + return binascii.hexlify(x) + else: + return x.encode('hex') + + def user_dir(): if 'ANDROID_DATA' in os.environ: return android_check_data_dir() @@ -251,12 +440,14 @@ def user_dir(): #raise Exception("No home directory found in environment variables.") return + def format_satoshis_plain(x, decimal_point = 8): - '''Display a satoshi amount scaled. Always uses a '.' as a decimal - point and has no thousands separator''' + """Display a satoshi amount scaled. Always uses a '.' as a decimal + point and has no thousands separator""" scale_factor = pow(10, decimal_point) return "{:.8f}".format(Decimal(x) / scale_factor).rstrip('0').rstrip('.') + def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespaces=False): from locale import localeconv if x is None: @@ -277,7 +468,9 @@ def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespa if whitespaces: result += " " * (decimal_point - len(fract_part)) result = " " * (15 - len(result)) + result - return result.decode('utf8') + if six.PY2: + result = result.decode('utf8') + return result def timestamp_to_datetime(timestamp): try: @@ -404,15 +597,15 @@ def block_explorer_URL(config, kind, item): #urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) def parse_URI(uri, on_pr=None): - import bitcoin - from bitcoin import COIN + from . import bitcoin + from .bitcoin import COIN if ':' not in uri: if not bitcoin.is_address(uri): raise BaseException("Not a bitcoin address") return {'address': uri} - u = urlparse.urlparse(uri) + u = urllib_parse.urlparse(uri) if u.scheme != 'bitcoin': raise BaseException("Not a bitcoin URI") address = u.path @@ -420,9 +613,9 @@ def parse_URI(uri, on_pr=None): # python for android fails to parse query if address.find('?') > 0: address, query = u.path.split('?') - pq = urlparse.parse_qs(query) + pq = urllib_parse.parse_qs(query) else: - pq = urlparse.parse_qs(u.query) + pq = urllib_parse.parse_qs(u.query) for k, v in pq.items(): if len(v)!=1: @@ -443,21 +636,24 @@ def parse_URI(uri, on_pr=None): amount = Decimal(am) * COIN out['amount'] = int(amount) if 'message' in out: - out['message'] = out['message'].decode('utf8') + if six.PY3: + out['message'] = out['message'] + else: + out['message'] = out['message'].decode('utf8') out['memo'] = out['message'] if 'time' in out: out['time'] = int(out['time']) if 'exp' in out: out['exp'] = int(out['exp']) if 'sig' in out: - out['sig'] = bitcoin.base_decode(out['sig'], None, base=58).encode('hex') + out['sig'] = bh2u(bitcoin.base_decode(out['sig'], None, base=58)) r = out.get('r') sig = out.get('sig') name = out.get('name') if on_pr and (r or (name and sig)): def get_payment_request_thread(): - import paymentrequest as pr + from . import paymentrequest as pr if name and sig: s = pr.serialize_request(out).SerializeToString() request = pr.PaymentRequest(s) @@ -472,7 +668,7 @@ def parse_URI(uri, on_pr=None): def create_URI(addr, amount, message): - import bitcoin + from . import bitcoin if not bitcoin.is_address(addr): return "" query = [] @@ -482,19 +678,26 @@ def create_URI(addr, amount, message): if type(message) == unicode: message = message.encode('utf8') query.append('message=%s'%urllib.quote(message)) - p = urlparse.ParseResult(scheme='bitcoin', netloc='', path=addr, params='', query='&'.join(query), fragment='') - return urlparse.urlunparse(p) + p = urllib_parse.ParseResult(scheme='bitcoin', netloc='', path=addr, params='', query='&'.join(query), fragment='') + return urllib_parse.urlunparse(p) # Python bug (http://bugs.python.org/issue1927) causes raw_input # to be redirected improperly between stdin/stderr on Unix systems +#TODO: py3 def raw_input(prompt=None): if prompt: sys.stdout.write(prompt) return builtin_raw_input() -import __builtin__ -builtin_raw_input = __builtin__.raw_input -__builtin__.raw_input = raw_input + +if six.PY2: + import __builtin__ + builtin_raw_input = __builtin__.raw_input + __builtin__.raw_input = raw_input +else: + import builtins + builtin_raw_input = builtins.input + builtins.input = raw_input @@ -596,8 +799,6 @@ class SocketPipe: -import Queue - class QueuePipe: def __init__(self, send_queue=None, get_queue=None): diff --git a/lib/verifier.py b/lib/verifier.py @@ -22,10 +22,14 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals - -from util import ThreadJob -from bitcoin import * +import six +from .util import ThreadJob +from .bitcoin import * class SPV(ThreadJob): diff --git a/lib/wallet.py b/lib/wallet.py @@ -23,13 +23,11 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -""" -Wallet classes: - - Imported_Wallet: imported address, no keystore - - Standard_Wallet: one keystore, P2PKH - - Multisig_Wallet: several keystores, P2SH +# Wallet classes: +# - Imported_Wallet: imported address, no keystore +# - Standard_Wallet: one keystore, P2PKH +# - Multisig_Wallet: several keystores, P2SH -""" import os import hashlib @@ -45,27 +43,26 @@ import errno from functools import partial from collections import namedtuple, defaultdict -from i18n import _ -from util import NotEnoughFunds, PrintError, UserCancelled, profiler +from .i18n import _ +from .util import NotEnoughFunds, PrintError, UserCancelled, profiler -from bitcoin import * -from version import * -from keystore import load_keystore, Hardware_KeyStore -from storage import multisig_type +from .bitcoin import * +from .version import * +from .keystore import load_keystore, Hardware_KeyStore +from .storage import multisig_type import transaction -from transaction import Transaction -from plugins import run_hook -import bitcoin -import coinchooser -from synchronizer import Synchronizer -from verifier import SPV -from mnemonic import Mnemonic +from .transaction import Transaction +from .plugins import run_hook +from . import bitcoin +from . import coinchooser +from .synchronizer import Synchronizer +from .verifier import SPV +from .mnemonic import Mnemonic -import paymentrequest -from paymentrequest import InvoiceStore -from contacts import Contacts +from . import paymentrequest +from .storage import WalletStorage TX_STATUS = [ _('Replaceable'), @@ -197,11 +194,11 @@ class Abstract_Wallet(PrintError): @profiler def check_history(self): save = False - for addr, hist in self.history.items(): - if not self.is_mine(addr): - self.history.pop(addr) - save = True - continue + mine_addrs = list(filter(lambda k: self.is_mine(self.history[k]), self.history.keys())) + if len(mine_addrs) != len(self.history.keys()): + save = True + for addr in mine_addrs: + hist = self.history[addr] for tx_hash, tx_height in hist: if tx_hash in self.pruned_txo.values() or self.txi.get(tx_hash) or self.txo.get(tx_hash): @@ -625,7 +622,7 @@ class Abstract_Wallet(PrintError): if _type == TYPE_ADDRESS: addr = x elif _type == TYPE_PUBKEY: - addr = bitcoin.public_key_to_p2pkh(x.decode('hex')) + addr = bitcoin.public_key_to_p2pkh(bfh(x)) else: addr = None if addr and self.is_mine(addr): @@ -947,7 +944,7 @@ class Abstract_Wallet(PrintError): # if we are on a pruning server, remove unverified transactions with self.lock: - vr = self.verified_tx.keys() + self.unverified_tx.keys() + vr = list(self.verified_tx.keys()) + list(self.unverified_tx.keys()) for tx_hash in self.transactions.keys(): if tx_hash not in vr: self.print_error("removing transaction", tx_hash) @@ -1253,7 +1250,7 @@ class Abstract_Wallet(PrintError): def make_payment_request(self, addr, amount, message, expiration): timestamp = int(time.time()) - _id = Hash(addr + "%d"%timestamp).encode('hex')[0:10] + _id = bh2u(Hash(addr + "%d"%timestamp))[0:10] r = {'time':timestamp, 'amount':amount, 'exp':expiration, 'address':addr, 'memo':message, 'id':_id} return r @@ -1263,7 +1260,7 @@ class Abstract_Wallet(PrintError): pr = paymentrequest.make_unsigned_request(req) paymentrequest.sign_request_with_alias(pr, alias, alias_privkey) req['name'] = pr.pki_data - req['sig'] = pr.signature.encode('hex') + req['sig'] = bh2u(pr.signature) self.receive_requests[key] = req self.storage.put('payment_requests', self.receive_requests) @@ -1420,7 +1417,10 @@ class Imported_Wallet(Abstract_Wallet): def add_input_sig_info(self, txin, address): addrtype, hash160 = bc_address_to_hash_160(address) - x_pubkey = 'fd' + (chr(addrtype) + hash160).encode('hex') + if six.PY3: + x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160) + else: + x_pubkey = 'fd' + bh2u(chr(addrtype) + hash160) txin['x_pubkeys'] = [x_pubkey] txin['signatures'] = [None] diff --git a/lib/websockets.py b/lib/websockets.py @@ -22,8 +22,14 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. - -import threading, Queue, os, json, time +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import six +from six.moves import queue +import threading, os, json, time from collections import defaultdict try: from SimpleWebSocketServer import WebSocket, SimpleSSLWebSocketServer @@ -31,9 +37,9 @@ except ImportError: import sys sys.exit("install SimpleWebSocketServer") -import util +from . import util -request_queue = Queue.Queue() +request_queue = queue.Queue() class ElectrumWebSocket(WebSocket): diff --git a/lib/x509.py b/lib/x509.py @@ -22,56 +22,63 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals - +import six from datetime import datetime import sys -import util -from util import profiler, print_error +from . import util +from .util import profiler, print_error import ecdsa import hashlib - # algo OIDs -ALGO_RSA_SHA1 = '1.2.840.113549.1.1.5' +ALGO_RSA_SHA1 = '1.2.840.113549.1.1.5' ALGO_RSA_SHA256 = '1.2.840.113549.1.1.11' ALGO_RSA_SHA384 = '1.2.840.113549.1.1.12' ALGO_RSA_SHA512 = '1.2.840.113549.1.1.13' ALGO_ECDSA_SHA256 = '1.2.840.10045.4.3.2' # prefixes, see http://stackoverflow.com/questions/3713774/c-sharp-how-to-calculate-asn-1-der-encoding-of-a-particular-hash-algorithm -PREFIX_RSA_SHA256 = bytearray([0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20]) -PREFIX_RSA_SHA384 = bytearray([0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30]) -PREFIX_RSA_SHA512 = bytearray([0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40]) +PREFIX_RSA_SHA256 = bytearray( + [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]) +PREFIX_RSA_SHA384 = bytearray( + [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30]) +PREFIX_RSA_SHA512 = bytearray( + [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40]) # types used in ASN1 structured data ASN1_TYPES = { - 'BOOLEAN': 0x01, - 'INTEGER': 0x02, - 'BIT STRING': 0x03, - 'OCTET STRING': 0x04, - 'NULL': 0x05, + 'BOOLEAN' : 0x01, + 'INTEGER' : 0x02, + 'BIT STRING' : 0x03, + 'OCTET STRING' : 0x04, + 'NULL' : 0x05, 'OBJECT IDENTIFIER': 0x06, - 'SEQUENCE': 0x70, - 'SET': 0x71, - 'PrintableString': 0x13, - 'IA5String': 0x16, - 'UTCTime': 0x17, - 'ENUMERATED': 0x0A, - 'UTF8String': 0x0C, - 'PrintableString': 0x13, + 'SEQUENCE' : 0x70, + 'SET' : 0x71, + 'PrintableString' : 0x13, + 'IA5String' : 0x16, + 'UTCTime' : 0x17, + 'ENUMERATED' : 0x0A, + 'UTF8String' : 0x0C, } + class CertificateError(Exception): pass -# helper functions +# helper functions def bitstr_to_bytestr(s): if s[0] != '\x00': raise BaseException('no padding') return s[1:] + def bytestr_to_int(s): i = 0 for char in s: @@ -79,6 +86,7 @@ def bytestr_to_int(s): i |= ord(char) return i + def decode_OID(s): s = map(ord, s) r = [] @@ -87,103 +95,109 @@ def decode_OID(s): k = 0 for i in s[1:]: if i < 128: - r.append(i + 128*k) + r.append(i + 128 * k) k = 0 else: - k = (i - 128) + 128*k + k = (i - 128) + 128 * k return '.'.join(map(str, r)) def encode_OID(oid): x = map(int, oid.split('.')) - s = chr(x[0]*40 + x[1]) + s = chr(x[0] * 40 + x[1]) for i in x[2:]: ss = chr(i % 128) while i > 128: - i = i / 128 + i //= 128 ss = chr(128 + i % 128) + ss s += ss return s - - class ASN1_Node(str): - 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 = ord(self[ix + 1]) + if (ord(self[ix + 1]) & 0x80) == 0: length = first ixf = ix + 2 ixl = ixf + length - 1 - else: + else: lengthbytes = first & 0x7F - length = bytestr_to_int(self[ix+2:ix+2+lengthbytes]) + length = bytestr_to_int(self[ix + 2:ix + 2 + lengthbytes]) ixf = ix + 2 + lengthbytes - 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) + 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) nodes.append(ii) - while ii[2] < node[2]: - ii = self.next_node(ii) - 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 + return nodes -class X509(object): +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 + + +class X509(object): def __init__(self, b): self.bytes = bytearray(b) @@ -292,24 +306,21 @@ class X509(object): not_before = time.mktime(time.strptime(self.notBefore, TIMESTAMP_FMT)) not_after = time.mktime(time.strptime(self.notAfter, TIMESTAMP_FMT)) if not_before > now: - raise CertificateError('Certificate has not entered its valid date range. (%s)'%self.get_common_name()) + raise CertificateError('Certificate has not entered its valid date range. (%s)' % self.get_common_name()) if not_after <= now: - raise CertificateError('Certificate has expired. (%s)'%self.get_common_name()) + raise CertificateError('Certificate has expired. (%s)' % self.get_common_name()) def getFingerprint(self): return hashlib.sha1(self.bytes).digest() - - - @profiler def load_certificates(ca_path): - import pem + from . import pem ca_list = {} ca_keyID = {} - with open(ca_path, 'r') as f: - s = f.read() + with open(ca_path, 'rb') as f: + s = f.read().decode('utf8') bList = pem.dePemList(s, "CERTIFICATE") for b in bList: try: @@ -328,7 +339,7 @@ def load_certificates(ca_path): if __name__ == "__main__": import requests + util.set_verbosity(True) ca_path = requests.certs.where() ca_list, ca_keyID = load_certificates(ca_path) -