Browse Source

py3

seed_v14
Dmitry Sorokin 8 years ago
committed by ThomasV
parent
commit
5be78950ca
  1. 37
      electrum
  2. 16
      gui/qt/__init__.py
  3. 12
      gui/qt/address_dialog.py
  4. 7
      gui/qt/address_list.py
  5. 6
      gui/qt/amountedit.py
  6. 8
      gui/qt/console.py
  7. 7
      gui/qt/contact_list.py
  8. 6
      gui/qt/fee_slider.py
  9. 7
      gui/qt/history_list.py
  10. 14
      gui/qt/installwizard.py
  11. 7
      gui/qt/invoice_list.py
  12. 46
      gui/qt/main_window.py
  13. 11
      gui/qt/network_dialog.py
  14. 7
      gui/qt/password_dialog.py
  15. 9
      gui/qt/paytoedit.py
  16. 8
      gui/qt/qrcodewidget.py
  17. 8
      gui/qt/qrtextedit.py
  18. 6
      gui/qt/qrwindow.py
  19. 7
      gui/qt/request_list.py
  20. 9
      gui/qt/seed_dialog.py
  21. 7
      gui/qt/transaction_dialog.py
  22. 10
      gui/qt/util.py
  23. 7
      gui/qt/utxo_list.py
  24. 6
      gui/stdio.py
  25. 6
      gui/text.py
  26. 28
      lib/__init__.py
  27. 0
      lib/account.py
  28. 17
      lib/base_wizard.py
  29. 236
      lib/bitcoin.py
  30. 11
      lib/blockchain.py
  31. 13
      lib/coinchooser.py
  32. 31
      lib/commands.py
  33. 14
      lib/contacts.py
  34. 52
      lib/daemon.py
  35. 31
      lib/dnssec.py
  36. 17
      lib/exchange_rate.py
  37. 12
      lib/i18n.py
  38. 34
      lib/interface.py
  39. 62
      lib/keystore.py
  40. 19
      lib/mnemonic.py
  41. 16
      lib/msqr.py
  42. 36
      lib/network.py
  43. 20
      lib/old_mnemonic.py
  44. 45
      lib/paymentrequest.py
  45. 7
      lib/pem.py
  46. 16
      lib/plugins.py
  47. 8
      lib/rsakey.py
  48. 21
      lib/simple_config.py
  49. 15
      lib/storage.py
  50. 12
      lib/synchronizer.py
  51. 6
      lib/tests/test_account.py
  52. 13
      lib/tests/test_bitcoin.py
  53. 6
      lib/tests/test_interface.py
  54. 10
      lib/tests/test_mnemonic.py
  55. 17
      lib/tests/test_simple_config.py
  56. 22
      lib/tests/test_transaction.py
  57. 6
      lib/tests/test_util.py
  58. 10
      lib/tests/test_wallet.py
  59. 200
      lib/transaction.py
  60. 237
      lib/util.py
  61. 10
      lib/verifier.py
  62. 62
      lib/wallet.py
  63. 14
      lib/websockets.py
  64. 55
      lib/x509.py

37
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,7 +308,7 @@ 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':
@ -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
@ -427,7 +438,7 @@ if __name__ == '__main__':
result = run_offline_command(config, config_options)
# print result
if type(result) in [str, unicode]:
if isinstance(result, six.text_type):
print_msg(result)
elif type(result) is dict and result.get('error'):
print_stderr(result.get('error'))

16
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):

12
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):

7
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

6
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()

8
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:

7
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):

6
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

7
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

14
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):

7
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

46
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

11
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'

7
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

9
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,})\>'

8
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):

8
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):

6
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 *

7
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):

9
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):

7
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...

10
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()

7
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

6
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 _

6
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

28
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

0
lib/account.py

17
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):

236
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:
assert_bytes(v)
assert base in (58, 43)
chars = __b58chars
elif base == 43:
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:
# assert_bytes(v)
v = to_bytes(v, 'ascii')
assert base in (58, 43)
chars = __b58chars
elif base == 43:
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

11
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

13
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)

31
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,

14
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):

52
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):

31
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)

17
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)

12
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)
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

34
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()

62
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,14 +589,17 @@ 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']:
@ -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)

19
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
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)

16
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

36
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,6 +441,7 @@ 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:
if six.PY2:
socket.socket = socket._socketobject
socket.getaddrinfo = socket._socket.getaddrinfo
@ -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'))

20
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
@ -1689,8 +1695,8 @@ 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'
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]))
print(' '.join(mn_encode(sys.argv[1])))
else:
print mn_decode(sys.argv[1:])
print(mn_decode(sys.argv[1:]))

45
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)

7
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):

16
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':

8
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):

21
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

15
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

12
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):

6
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):

13
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)

6
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

10
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')

17
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)

22
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):

6
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

10
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):

200
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

237
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,6 +636,9 @@ def parse_URI(uri, on_pr=None):
amount = Decimal(am) * COIN
out['amount'] = int(amount)
if 'message' in out:
if six.PY3:
out['message'] = out['message']
else:
out['message'] = out['message'].decode('utf8')
out['memo'] = out['message']
if 'time' in out:
@ -450,14 +646,14 @@ def parse_URI(uri, on_pr=None):
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()
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):

10
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):

62
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)
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
continue
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]

14
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):

55
lib/x509.py

@ -22,16 +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 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_SHA256 = '1.2.840.113549.1.1.11'
@ -40,9 +43,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 = {
@ -59,19 +65,20 @@ ASN1_TYPES = {
'UTCTime' : 0x17,
'ENUMERATED' : 0x0A,
'UTF8String' : 0x0C,
'PrintableString': 0x13,
}
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 = []
@ -100,16 +108,13 @@ def encode_OID(oid):
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])
@ -122,31 +127,37 @@ class ASN1_Node(str):
length = bytestr_to_int(self[ix + 2:ix + 2 + lengthbytes])
ixf = ix + 2 + lengthbytes
ixl = ixf + length - 1
return (ix, ixf, ixl)
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
@ -154,10 +165,12 @@ class ASN1_Node(str):
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)
@ -167,9 +180,11 @@ class ASN1_Node(str):
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):
@ -183,7 +198,6 @@ class ASN1_Node(str):
class X509(object):
def __init__(self, b):
self.bytes = bytearray(b)
@ -300,16 +314,13 @@ class X509(object):
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)

Loading…
Cancel
Save