commit 5be78950ca31ba903b4bd0f575511eeb732e114c
parent f70408cef5eff3eead108a06ee11a6106253f74d
Author: Dmitry Sorokin <asfins@gmail.com>
Date: Sun, 22 Jan 2017 21:25:24 +0300
py3
Diffstat:
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)
-