merge upstream, fix setup.py conflict

This commit is contained in:
Riccardo Spagni 2015-02-04 16:03:03 +02:00
commit ab6837485f
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
35 changed files with 452 additions and 317 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ gui/qt/icons_rc.py
locale/
.devlocaltmp/
*_trial_temp
packages

View File

@ -7,6 +7,7 @@ recursive-include lib *.py
recursive-include gui *.py
recursive-include plugins *.py
recursive-include packages *.py
recursive-include packages cacert.pem
include app.fil
include icons.qrc
recursive-include icons *

View File

@ -1,28 +1,32 @@
# Release 2.0
* New address derivation (BIP32 + BIP44).
* New address derivation (BIP32).
* 8 bits of the seed phrase are used to store a version number. The
current version number (0x01) refers to the current wallet
structure (BIP44). The version number also serves as a checksum for
the seed, and it will prevent the import of seeds from incompatible
wallets.
* New seed phrase format: 8 bits of the seed phrase are used to store
a version number. The current version number (0x01) refers to the
default wallet structure. The version number also serves as a
checksum for the seed, and it will prevent the import of seeds from
incompatible wallets.
* New serialization format for unsigned or partially signed
* Compact serialization format for unsigned or partially signed
transactions, that includes the master public key and derivation
needed to sign inputs. This new format is compact enough to
send transactions to cold storage using QR codes
needed to sign inputs. This allows to send partially signed
transactions using QR codes
* Deterministic Multisig wallets using parallel BIP32 derivations and
P2SH addresses (2 of 2, 2 of 3).
* New plugins:
* New plugins:
- TrustedCoin: two-factor authentication using 2 of 3 multisig and
Google Authenticator
- Trezor: support for the Trezor hardware wallet by SatoshiLabs
- Cosigner Pool: shared memory pool for partially signed transactions
- Cosigner Pool: encrypted communication channel for multisig
wallets, to send and receive partially signed transactions
- Audio Modem: send and receive transactions by sound
* BIP70: verification of signed payment requests
- Verification is pure python, using tlslite.
- In the GUI, payment requests are in the 'Invoices' tab.
* Support for BIP70: payment requests
- Verification of the signature chain uses tlslite.
- In the GUI, payment requests are shown in the 'Invoices' tab.
* New 'Receive' tab:
- create and manage payment requests, with QR Codes
@ -31,7 +35,7 @@
window that pops up if you click on the QR code
* The 'Send' tab in the Qt GUI supports transactions with multiple
outputs, and with OP_RETURN "message"
outputs, and raw hexadecimal scripts.
* The GUI can use the daemon: "electrum -d". The daemon can serve
several clients. It times out if no client uses if for more than 5
@ -42,8 +46,15 @@
* Wallet files are saved as JSON instead of Python.
* Client supports servers with SSL certificates signed by a CA.
* Documentation is now hosted on a wiki: http://electrum.orain.org
* ECIES encrypt/decrypt methods, availabe in the GUI and using the
command line:
encrypt <pubkey> <message>
decrypt <pubkey> <message>
# Release 1.9.8

View File

@ -1,23 +1,20 @@
#!/usr/bin/python
from lib.version import ELECTRUM_VERSION as version
if __name__ == '__main__':
import sys, re, shutil, os, hashlib
import imp
os.chdir(os.path.dirname(os.path.realpath(__file__)))
os.chdir('..')
imp.load_module('electrum', *imp.find_module('../lib'))
from electrum.version import ELECTRUM_VERSION as version
if not ( os.path.exists('packages')):
print "The packages directory is missing."
sys.exit()
# os.system("python mki18n.py")
os.system("pyrcc4 icons.qrc -o gui/qt/icons_rc.py")
os.system("python setup.py sdist --format=zip,gztar")
_tgz="Electrum-%s.tar.gz"%version
_zip="Electrum-%s.zip"%version
# android
os.system('rm -rf dist/e4a-%s'%version)
os.mkdir('dist/e4a-%s'%version)
shutil.copyfile("electrum",'dist/e4a-%s/e4a.py'%version)
@ -37,21 +34,6 @@ if __name__ == '__main__':
e4a_name = "e4a-%s.zip"%version
e4a_name2 = e4a_name.replace(".","")
os.system( "mv %s %s"%(e4a_name, e4a_name2) )
print "dist/%s "%e4a_name2
import getpass
password = getpass.getpass("Password:")
for f in os.listdir("."):
os.system( "gpg --sign --armor --detach --passphrase \"%s\" %s"%(password, f) )
md5_tgz = hashlib.md5(file(_tgz, 'r').read()).digest().encode('hex')
md5_zip = hashlib.md5(file(_zip, 'r').read()).digest().encode('hex')
md5_android = hashlib.md5(file(e4a_name2, 'r').read()).digest().encode('hex')
os.chdir("..")
print ""
print "Packages are ready:"
print "dist/%s "%_tgz, md5_tgz
print "dist/%s "%_zip, md5_zip
print "dist/%s "%e4a_name2, md5_android
print "To make a release, upload the files to the server, and update the webpages in branch gh-pages"

40
contrib/make_packages Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/python
import sys, re, shutil, os, hashlib
import imp
import getpass
if __name__ == '__main__':
os.chdir(os.path.dirname(os.path.realpath(__file__)))
os.chdir('..')
imp.load_module('electrum', *imp.find_module('../lib'))
from electrum.version import ELECTRUM_VERSION as version
if not ( os.path.exists('packages')):
print "The packages directory is missing."
sys.exit()
# os.system("python mki18n.py")
os.system("pyrcc4 icons.qrc -o gui/qt/icons_rc.py")
os.system("python setup.py sdist --format=zip,gztar")
_tgz="Electrum-%s.tar.gz"%version
_zip="Electrum-%s.zip"%version
os.chdir("dist")
password = getpass.getpass("Password:")
for f in [_tgz,_zip]:
os.system( "gpg --sign --armor --detach --passphrase \"%s\" %s"%(password, f) )
md5_tgz = hashlib.md5(file(_tgz, 'r').read()).digest().encode('hex')
md5_zip = hashlib.md5(file(_zip, 'r').read()).digest().encode('hex')
os.chdir("..")
print ""
print "Packages are ready:"
print "dist/%s "%_tgz, md5_tgz
print "dist/%s "%_zip, md5_zip
print "To make a release, upload the files to the server, and update the webpages in branch gh-pages"

View File

@ -26,18 +26,54 @@ import sys
import time
import traceback
is_local = os.path.dirname(os.path.realpath(__file__)) == os.getcwd()
is_bundle = getattr(sys, 'frozen', False)
is_local = not is_bundle and os.path.dirname(os.path.realpath(__file__)) == os.getcwd()
is_android = 'ANDROID_DATA' in os.environ
if is_local:
sys.path.append('packages')
sys.path.insert(0, 'packages')
elif is_bundle and sys.platform=='darwin':
sys.path.insert(0, os.getcwd() + "/lib/python2.7/packages")
import __builtin__
__builtin__.use_local_modules = is_local or is_android
# pure-python dependencies need to be imported here for pyinstaller
try:
import aes
import ecdsa
import socks
import requests
import six
import qrcode
import pyasn1
import pyasn1_modules
import tlslite
import pbkdf2
import google.protobuf
except ImportError as e:
sys.exit("Error: %s. Try 'sudo pip install <module-name>'"%e.message)
# the following imports are for pyinstaller
import pyasn1.codec
import pyasn1.codec.der
from pyasn1.codec.der import encoder, decoder
from pyasn1_modules import rfc2459
from google.protobuf import descriptor
from google.protobuf import message
from google.protobuf import reflection
from google.protobuf import descriptor_pb2
# check that we have the correct version of ecdsa
try:
from ecdsa.ecdsa import curve_secp256k1, generator_secp256k1
except Exception:
sys.exit("cannot import ecdsa.curve_secp256k1. You probably need to upgrade ecdsa.\nTry: sudo pip install --upgrade ecdsa")
# load local module as electrum
if __builtin__.use_local_modules:
if is_bundle or is_local or is_android:
import imp
imp.load_module('electrum', *imp.find_module('lib'))
imp.load_module('electrum_gui', *imp.find_module('gui'))
@ -45,12 +81,11 @@ if __builtin__.use_local_modules:
from electrum import util
from electrum import SimpleConfig, Network, Wallet, WalletStorage, NetworkProxy, Commands, known_commands, pick_random_server
from electrum.util import print_msg, print_stderr, print_json, set_verbosity, InvalidPassword
from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
from electrum.daemon import get_daemon
from electrum.plugins import init_plugins
# get password routine
def prompt_password(prompt, confirm=True):
import getpass
@ -170,10 +205,12 @@ if __name__ == '__main__':
for k, v in config_options.items():
if v is None:
config_options.pop(k)
if config_options.get('server'):
config_options['auto_cycle'] = False
set_verbosity(config_options.get('verbose'))
config = SimpleConfig(config_options)
print_error("CA bundle:", requests.utils.DEFAULT_CA_BUNDLE_PATH, "found" if os.path.exists(requests.utils.DEFAULT_CA_BUNDLE_PATH) else "not found")
if len(args) == 0:
url = None
@ -185,7 +222,6 @@ if __name__ == '__main__':
cmd = args[0]
if cmd == 'gui':
init_plugins(config)
gui_name = config.get('gui', 'classic')
if gui_name in ['lite', 'classic']:
gui_name = 'qt'
@ -196,6 +232,9 @@ if __name__ == '__main__':
sys.exit()
#sys.exit("Error: Unknown GUI: " + gui_name )
if gui_name=='qt':
init_plugins(config, is_bundle or is_local or is_android)
# network interface
if not options.offline:
s = get_daemon(config, start_daemon=options.daemon)

24
electrum-env Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
#
# This script creates a virtualenv named 'env' and installs all
# python dependencies before activating the env and running Electrum.
# If 'env' already exists, it is activated and Electrum is started
# without any installations. Additionally, the PYTHONPATH environment
# variable is set properly before running Electrum.
#
# python-qt and its dependencies will still need to be installed with
# your package manager.
if [ -e ./env/bin/activate ]; then
source ./env/bin/activate
else
virtualenv env
source ./env/bin/activate
python setup.py install
fi
export PYTHONPATH=/usr/local/lib/python2.7/site-packages:$PYTHONPATH
./electrum
deactivate

View File

@ -35,7 +35,7 @@ class InstallWizard(QDialog):
self.storage = storage
self.setMinimumSize(575, 400)
self.setMaximumSize(575, 400)
self.setWindowTitle('Electrum')
self.setWindowTitle('Electrum' + ' - ' + os.path.basename(self.storage.path))
self.connect(self, QtCore.SIGNAL('accept'), self.accept)
self.stack = QStackedLayout()
self.setLayout(self.stack)
@ -142,10 +142,8 @@ class InstallWizard(QDialog):
def multi_mpk_dialog(self, xpub_hot, n):
vbox = QVBoxLayout()
vbox0, seed_e0 = seed_dialog.enter_seed_box(MSG_SHOW_MPK, self, 'hot')
vbox0 = seed_dialog.show_seed_box(MSG_SHOW_MPK, xpub_hot, 'hot')
vbox.addLayout(vbox0)
seed_e0.setText(xpub_hot)
seed_e0.setReadOnly(True)
entries = []
for i in range(n):
vbox2, seed_e2 = seed_dialog.enter_seed_box(MSG_ENTER_COLD_MPK, self, 'cold')
@ -308,7 +306,7 @@ class InstallWizard(QDialog):
def show_seed(self, seed, sid):
vbox = seed_dialog.show_seed_box(seed, sid)
vbox = seed_dialog.show_seed_box_msg(seed, sid)
vbox.addLayout(ok_cancel_buttons(self, _("Next")))
self.set_layout(vbox)
return self.exec_()

View File

@ -203,6 +203,7 @@ class ElectrumWindow(QMainWindow):
def close_wallet(self):
self.wallet.stop_threads()
self.hide()
run_hook('close_wallet')
def load_wallet(self, wallet):
@ -210,13 +211,17 @@ class ElectrumWindow(QMainWindow):
self.wallet = wallet
self.update_wallet_format()
# address used to create a dummy transaction and estimate transaction fee
self.dummy_address = self.wallet.addresses(False)[0]
a = self.wallet.addresses(False)
self.dummy_address = a[0] if a else None
self.invoices = self.wallet.storage.get('invoices', {})
self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{})
self.current_account = self.wallet.storage.get("current_account", None)
title = 'Electrum ' + self.wallet.electrum_version + ' - ' + os.path.basename(self.wallet.storage.path)
if self.wallet.is_watching_only(): title += ' [%s]' % (_('watching only'))
self.setWindowTitle( title )
self.update_history_tab()
self.show()
self.update_wallet()
# Once GUI has been initialized check if we want to announce something since the callback has been called before the GUI was initialized
self.notify_transactions()
@ -254,17 +259,42 @@ class ElectrumWindow(QMainWindow):
filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) )
if not filename:
return
storage = WalletStorage({'wallet_path': filename})
if not storage.file_exists:
self.show_message("file not found "+ filename)
try:
storage = WalletStorage({'wallet_path': filename})
except Exception as e:
self.show_message(str(e))
return
if not storage.file_exists:
self.show_message(_("File not found") + ' ' + filename)
return
# read wizard action
try:
wallet = Wallet(storage)
except BaseException as e:
QMessageBox.warning(None, _('Warning'), str(e), _('OK'))
return
action = wallet.get_action()
# ask for confirmation
if action is not None:
if not self.question(_("This file contains an incompletely created wallet.\nDo you want to complete its creation now?")):
return
# close current wallet
self.close_wallet()
# load new wallet
wallet = Wallet(storage)
wallet.start_threads(self.network)
# run wizard
if action is not None:
import installwizard
wizard = installwizard.InstallWizard(self.config, self.network, storage)
try:
wallet = wizard.run(action)
except BaseException as e:
traceback.print_exc(file=sys.stdout)
QMessageBox.information(None, _('Error'), str(e), _('OK'))
return
if not wallet:
return
else:
wallet.start_threads(self.network)
# load new wallet in gui
self.load_wallet(wallet)
@ -308,6 +338,8 @@ class ElectrumWindow(QMainWindow):
QMessageBox.critical(None, "Error", _("File exists"))
return
if self.wallet:
self.close_wallet()
wizard = installwizard.InstallWizard(self.config, self.network, storage)
wallet = wizard.run('new')
if wallet:
@ -371,7 +403,7 @@ class ElectrumWindow(QMainWindow):
help_menu.addAction(_("&About"), self.show_about)
help_menu.addAction(_("&Official website"), lambda: webbrowser.open("http://electrum.org"))
help_menu.addSeparator()
help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://electrum.org/documentation.html")).setShortcut(QKeySequence.HelpContents)
help_menu.addAction(_("&Documentation"), lambda: webbrowser.open("http://electrum.orain.org/")).setShortcut(QKeySequence.HelpContents)
help_menu.addAction(_("&Report Bug"), self.show_report_bug)
self.setMenuBar(menubar)
@ -1278,7 +1310,7 @@ class ElectrumWindow(QMainWindow):
if not request_url:
if label:
if self.wallet.labels.get(address) != label:
if self.question(_('Save label "%s" for address %s ?'%(label,address))):
if self.question(_('Save label "%(label)s" for address %(address)s ?'%{'label':label,'address':address})):
if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
self.wallet.addressbook.append(address)
self.wallet.set_label(address, label)
@ -2507,7 +2539,7 @@ class ElectrumWindow(QMainWindow):
with open(fileName, "w+") as f:
if is_csv:
transaction = csv.writer(f)
transaction = csv.writer(f, lineterminator='\n')
transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
for line in lines:
transaction.writerow(line)

View File

@ -122,14 +122,15 @@ class NetworkDialog(QDialog):
lambda x,y: self.server_changed(x))
grid.addWidget(self.servers_list_widget, 1, 1, 1, 3)
if not config.is_modifiable('server'):
for w in [self.server_host, self.server_port, self.server_protocol, self.servers_list_widget]: w.setEnabled(False)
def enable_set_server():
enabled = not self.autocycle_cb.isChecked()
self.server_host.setEnabled(enabled)
self.server_port.setEnabled(enabled)
self.servers_list_widget.setEnabled(enabled)
if config.is_modifiable('server'):
enabled = not self.autocycle_cb.isChecked()
self.server_host.setEnabled(enabled)
self.server_port.setEnabled(enabled)
self.servers_list_widget.setEnabled(enabled)
else:
for w in [self.autocycle_cb, self.server_host, self.server_port, self.server_protocol, self.servers_list_widget]:
w.setEnabled(False)
self.autocycle_cb.clicked.connect(enable_set_server)
enable_set_server()
@ -143,19 +144,18 @@ class NetworkDialog(QDialog):
self.proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
def check_for_disable(index = False):
if self.proxy_mode.currentText() != 'NONE':
self.proxy_host.setEnabled(True)
self.proxy_port.setEnabled(True)
if self.config.is_modifiable('proxy'):
if self.proxy_mode.currentText() != 'NONE':
self.proxy_host.setEnabled(True)
self.proxy_port.setEnabled(True)
else:
self.proxy_host.setEnabled(False)
self.proxy_port.setEnabled(False)
else:
self.proxy_host.setEnabled(False)
self.proxy_port.setEnabled(False)
for w in [self.proxy_host, self.proxy_port, self.proxy_mode]: w.setEnabled(False)
check_for_disable()
self.proxy_mode.connect(self.proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
if not self.config.is_modifiable('proxy'):
for w in [self.proxy_host, self.proxy_port, self.proxy_mode]: w.setEnabled(False)
self.proxy_mode.setCurrentIndex(self.proxy_mode.findText(str(proxy_config.get("mode").upper())))
self.proxy_host.setText(proxy_config.get("host"))
self.proxy_port.setText(proxy_config.get("port"))

View File

@ -96,20 +96,21 @@ class PayToEdit(ScanQRTextEdit):
self.errors = []
if self.is_pr:
return
# filter out empty lines
lines = filter( lambda x: x, self.lines())
outputs = []
total = 0
self.payto_address = None
if len(lines) == 1:
data = lines[0]
if data.startswith("bitcoin:"):
self.scan_f(data)
return
try:
self.payto_address = self.parse_address(lines[0])
self.payto_address = self.parse_address(data)
except:
pass
if self.payto_address:
self.unlock_amount()
return

View File

@ -31,7 +31,7 @@ class SeedDialog(QDialog):
self.setModal(1)
self.setMinimumWidth(400)
self.setWindowTitle('Electrum' + ' - ' + _('Seed'))
vbox = show_seed_box(seed)
vbox = show_seed_box_msg(seed)
if imported_keys:
vbox.addWidget(QLabel("<b>"+_("WARNING")+":</b> " + _("Your wallet contains imported keys. These keys cannot be recovered from seed.") + "</b><p>"))
vbox.addLayout(close_button(self))
@ -47,71 +47,39 @@ def icon_filename(sid):
return ":icons/seed.png"
def show_seed_box(seed, sid=None):
save_msg = _("Please save these %d words on paper (order is important).")%len(seed.split()) + " "
qr_msg = _("Your seed is also displayed as QR code, in case you want to transfer it to a mobile phone.") + "<p>"
warning_msg = "<b>"+_("WARNING")+":</b> " + _("Never disclose your seed. Never type it on a website.") + "</b><p>"
if sid is None:
msg = _("Your wallet generation seed is")
msg2 = save_msg + " " \
+ _("This seed will allow you to recover your wallet in case of computer failure.") + "<br/>" \
+ warning_msg
elif sid == 'cold':
msg = _("Your cold storage seed is")
msg2 = save_msg + " " \
+ _("This seed will be permanently deleted from your wallet file. Make sure you have saved it before you press 'next'") + " " \
elif sid == 'hot':
msg = _("Your hot seed is")
msg2 = save_msg + " " \
+ _("If you ever need to recover your wallet from seed, you will need both this seed and your cold seed.") + " " \
label1 = QLabel(msg+ ":")
seed_text = ShowQRTextEdit(text=seed)
seed_text.setMaximumHeight(130)
def show_seed_box_msg(seedphrase, sid=None):
msg = _("Your wallet generation seed is") + ":"
vbox = show_seed_box(msg, seedphrase, sid)
save_msg = _("Please save these %d words on paper (order is important).")%len(seedphrase.split()) + " "
msg2 = save_msg + " " \
+ _("This seed will allow you to recover your wallet in case of computer failure.") + "<br/>" \
+ "<b>"+_("WARNING")+":</b> " + _("Never disclose your seed. Never type it on a website.") + "</b><p>"
label2 = QLabel(msg2)
label2.setWordWrap(True)
logo = QLabel()
logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
logo.setMaximumWidth(60)
grid = QGridLayout()
grid.addWidget(logo, 0, 0)
grid.addWidget(label1, 0, 1)
grid.addWidget(seed_text, 1, 0, 1, 2)
vbox = QVBoxLayout()
vbox.addLayout(grid)
vbox.addWidget(label2)
vbox.addStretch(1)
return vbox
def show_seed_box(msg, seed, sid):
vbox, seed_e = enter_seed_box(msg, None, sid=sid, text=seed)
return vbox
def enter_seed_box(msg, window, sid=None):
def enter_seed_box(msg, window, sid=None, text=None):
vbox = QVBoxLayout()
logo = QLabel()
logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
logo.setMaximumWidth(60)
label = QLabel(msg)
label.setWordWrap(True)
seed_e = ScanQRTextEdit(win=window)
seed_e.setMaximumHeight(100)
seed_e.setTabChangesFocus(True)
if not text:
seed_e = ScanQRTextEdit(win=window)
seed_e.setTabChangesFocus(True)
else:
seed_e = ShowQRTextEdit(text=text)
seed_e.setMaximumHeight(130)
vbox.addWidget(label)
grid = QGridLayout()
grid.addWidget(logo, 0, 0)
grid.addWidget(seed_e, 0, 1)
vbox.addLayout(grid)
return vbox, seed_e

View File

@ -156,7 +156,7 @@ class TxDialog(QDialog):
self.broadcast_button.show()
else:
s, r = self.tx.signature_count()
status = _("Unsigned") if s == 0 else _('Partially signed (%d/%d)'%(s,r))
status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r)
time_str = None
self.broadcast_button.hide()
tx_hash = 'unknown'

View File

@ -33,7 +33,7 @@ class VersionGetter(threading.Thread):
def run(self):
try:
con = httplib.HTTPConnection('electrum.org', 80, timeout=5)
con = httplib.HTTPSConnection('electrum.org', timeout=5)
con.request("GET", "/version")
res = con.getresponse()
except socket.error as msg:
@ -75,7 +75,10 @@ class UpdateLabel(QLabel):
def compare_versions(self, version1, version2):
def normalize(v):
return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
return cmp(normalize(version1), normalize(version2))
try:
return cmp(normalize(version1), normalize(version2))
except:
return 0
def ignore_this_version(self):
self.setText("")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -76,7 +76,7 @@ class Account(object):
return None
def synchronize_sequence(self, wallet, for_change):
limit = self.gap_limit_for_change if for_change else self.gap_limit
limit = wallet.gap_limit_for_change if for_change else wallet.gap_limit
while True:
addresses = self.get_addresses(for_change)
if len(addresses) < limit:
@ -175,14 +175,11 @@ class ImportedAccount(Account):
class OldAccount(Account):
""" Privatekey(type,n) = Master_private_key + H(n|S|type) """
gap_limit = 5
gap_limit_for_change = 3
def __init__(self, v):
Account.__init__(self, v)
self.mpk = v['mpk'].decode('hex')
@classmethod
def mpk_from_seed(klass, seed):
curve = SECP256k1
@ -274,8 +271,6 @@ class OldAccount(Account):
class BIP32_Account(Account):
gap_limit = 20
gap_limit_for_change = 3
def __init__(self, v):
Account.__init__(self, v)

View File

@ -26,15 +26,8 @@ import hmac
import version
from util import print_error, InvalidPassword
try:
import ecdsa
except ImportError:
sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
try:
import aes
except ImportError:
sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
import ecdsa
import aes
################################## transactions
@ -58,6 +51,8 @@ def strip_PKCS7_padding(s):
raise ValueError("Invalid PKCS7 padding")
return s[:-numpads]
# backport padding fix to AES module
aes.strip_PKCS7_padding = strip_PKCS7_padding
def aes_encrypt_with_iv(key, iv, data):
mode = aes.AESModeOfOperation.modeOfOperation["CBC"]
@ -401,12 +396,7 @@ def is_private_key(key):
########### end pywallet functions #######################
try:
from ecdsa.ecdsa import curve_secp256k1, generator_secp256k1
except Exception:
print "cannot import ecdsa.curve_secp256k1. You probably need to upgrade ecdsa.\nTry: sudo pip install --upgrade ecdsa"
exit()
from ecdsa.ecdsa import curve_secp256k1, generator_secp256k1
from ecdsa.curves import SECP256k1
from ecdsa.ellipticcurve import Point
from ecdsa.util import string_to_number, number_to_string

View File

@ -38,6 +38,30 @@ proxy_modes = ['socks4', 'socks5', 'http']
import util
def serialize_proxy(p):
if type(p) != dict:
return None
return ':'.join([p.get('mode'),p.get('host'), p.get('port')])
def deserialize_proxy(s):
if type(s) != str:
return None
if s.lower() == 'none':
return None
proxy = { "mode":"socks5", "host":"localhost" }
args = s.split(':')
n = 0
if proxy_modes.count(args[n]) == 1:
proxy["mode"] = args[n]
n += 1
if len(args) > n:
proxy["host"] = args[n]
n += 1
if len(args) > n:
proxy["port"] = args[n]
else:
proxy["port"] = "8080" if proxy["mode"] == "http" else "1080"
return proxy
def Interface(server, config = None):
@ -68,7 +92,7 @@ class TcpInterface(threading.Thread):
self.host, self.port, self.protocol = self.server.split(':')
self.port = int(self.port)
self.use_ssl = (self.protocol == 's')
self.proxy = self.parse_proxy_options(self.config.get('proxy'))
self.proxy = deserialize_proxy(self.config.get('proxy'))
if self.proxy:
self.proxy_mode = proxy_modes.index(self.proxy["mode"]) + 1
socks.setdefaultproxy(self.proxy_mode, self.proxy["host"], int(self.proxy["port"]))
@ -271,25 +295,6 @@ class TcpInterface(threading.Thread):
self.unanswered_requests[self.message_id] = method, params, _id, queue
self.message_id += 1
def parse_proxy_options(self, s):
if type(s) == type({}): return s # fixme: type should be fixed
if type(s) != type(""): return None
if s.lower() == 'none': return None
proxy = { "mode":"socks5", "host":"localhost" }
args = s.split(':')
n = 0
if proxy_modes.count(args[n]) == 1:
proxy["mode"] = args[n]
n += 1
if len(args) > n:
proxy["host"] = args[n]
n += 1
if len(args) > n:
proxy["port"] = args[n]
else:
proxy["port"] = "8080" if proxy["mode"] == "http" else "1080"
return proxy
def stop(self):
if self.is_connected and self.protocol in 'st' and self.s:
self.s.shutdown(socket.SHUT_RDWR)

View File

@ -1,4 +1,12 @@
import threading, time, Queue, os, sys, shutil, random
import threading
import time
import Queue
import os
import sys
import random
import traceback
from util import user_dir, appdata_dir, print_error, print_msg
from bitcoin import *
import interface
@ -178,7 +186,7 @@ class Network(threading.Thread):
def get_parameters(self):
host, port, protocol = self.default_server.split(':')
proxy = self.proxy
proxy = interface.deserialize_proxy(self.proxy)
auto_connect = self.config.get('auto_cycle', True)
return host, port, protocol, proxy, auto_connect
@ -225,14 +233,16 @@ class Network(threading.Thread):
threading.Thread.start(self)
def set_parameters(self, host, port, protocol, proxy, auto_connect):
proxy_str = interface.serialize_proxy(proxy)
server_str = ':'.join([ host, port, protocol ])
self.config.set_key('auto_cycle', auto_connect, True)
self.config.set_key("proxy", proxy, True)
self.config.set_key("proxy", proxy_str, True)
self.config.set_key("protocol", protocol, True)
server = ':'.join([ host, port, protocol ])
self.config.set_key("server", server, True)
self.config.set_key("server", server_str, True)
if self.proxy != proxy or self.protocol != protocol:
self.proxy = proxy
if self.proxy != proxy_str or self.protocol != protocol:
print_error('restarting network')
self.proxy = proxy_str
self.protocol = protocol
for i in self.interfaces.values(): i.stop()
if auto_connect:
@ -246,7 +256,7 @@ class Network(threading.Thread):
if self.server_is_lagging():
self.stop_interface()
else:
self.set_server(server)
self.set_server(server_str)
def switch_to_random_interface(self):
@ -358,6 +368,7 @@ class Network(threading.Thread):
out['result'] = f(*params)
except BaseException as e:
out['error'] = str(e)
traceback.print_exc(file=sys.stout)
print_error("network error", str(e))
self.response_queue.put(out)

View File

@ -27,18 +27,12 @@ import time
import traceback
import urllib2
import urlparse
import requests
try:
import paymentrequest_pb2
except:
sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=lib/ --python_out=lib/ lib/paymentrequest.proto'")
try:
import requests
except ImportError:
sys.exit("Error: requests does not seem to be installed. Try 'sudo pip install requests'")
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
@ -116,7 +110,7 @@ class PaymentRequest:
self.id = bitcoin.sha256(r)[0:16].encode('hex')
filename = os.path.join(self.dir_path, self.id)
with open(filename,'w') as f:
with open(filename,'wb') as f:
f.write(r)
return self.parse(r)
@ -131,7 +125,7 @@ class PaymentRequest:
def read_file(self, key):
filename = os.path.join(self.dir_path, key)
with open(filename,'r') as f:
with open(filename,'rb') as f:
r = f.read()
assert key == bitcoin.sha256(r)[0:16].encode('hex')

View File

@ -6,11 +6,11 @@ from i18n import _
plugins = []
def init_plugins(config):
def init_plugins(config, local):
import imp, pkgutil, __builtin__, os
global plugins
if __builtin__.use_local_modules:
if local:
fp, pathname, description = imp.find_module('plugins')
plugin_names = [name for a, name, b in pkgutil.iter_modules([pathname])]
plugin_names = filter( lambda name: os.path.exists(os.path.join(pathname,name+'.py')), plugin_names)
@ -40,21 +40,26 @@ def hook(func):
def run_hook(name, *args):
SPECIAL_HOOKS = ['get_wizard_action']
results = []
f_list = hooks.get(name,[])
for p, f in f_list:
if name == 'load_wallet':
p.wallet = args[0]
if not p.is_enabled():
continue
try:
r = f(*args)
except Exception:
print_error("Plugin error")
traceback.print_exc(file=sys.stdout)
r = False
if r:
results.append(r)
if name == 'init_qt':
gui = args[0]
p.window = gui.main_window
if name in SPECIAL_HOOKS or p.is_enabled():
try:
r = f(*args)
except Exception:
print_error("Plugin error")
traceback.print_exc(file=sys.stdout)
r = False
if r:
results.append(r)
if name == 'close_wallet':
p.wallet = None
if results:
assert len(results) == 1, results
@ -92,8 +97,12 @@ class BasePlugin:
def init_qt(self, gui): pass
@hook
def load_wallet(self, wallet): pass
@hook
def close_wallet(self): pass
#def init(self): pass
def close(self): pass

View File

@ -12,7 +12,7 @@ proc = None
def scan_qr(config):
global proc
if not zbar:
raise BaseException("\n".join([_("Cannot start QR scanner."),_("The zbar package is not available."),_("On Linux, try 'sudo apt-get install python-zbar'")]))
raise BaseException("\n".join([_("Cannot start QR scanner."),_("The zbar package is not available."),_("On Linux, try 'sudo pip install zbar'")]))
if proc is None:
device = config.get("video_device", "default")
if device == 'default':

View File

@ -110,7 +110,7 @@ class SimpleConfig(object):
out = None
with self.lock:
out = self.read_only_options.get(key)
if not out:
if out is None:
out = self.user_config.get(key, default)
return out

View File

@ -66,7 +66,16 @@ def data_dir():
if __builtin__.use_local_modules:
return local_data_dir()
else:
return appdata_dir()
is_frozen = getattr(sys, 'frozen', False)
if is_frozen:
if is_frozen == "macosx_app":
basedir = os.path.abspath(".")
else:
basedir = sys._MEIPASS
return os.path.join(basedir, 'data')
else:
return appdata_dir()
def usr_share_dir():
return os.path.join(sys.prefix, "share")

View File

@ -1,4 +1,4 @@
ELECTRUM_VERSION = "2.0" # version of the client package
ELECTRUM_VERSION = "2.0b2" # version of the client package
PROTOCOL_VERSION = '0.9' # protocol version requested
NEW_SEED_VERSION = 11 # electrum versions >= 2.0
OLD_SEED_VERSION = 4 # electrum versions < 2.0

View File

@ -1048,9 +1048,11 @@ class Abstract_Wallet(object):
addr = bitcoin.public_key_to_bc_address(x_pubkey.decode('hex'))
return self.is_mine(addr)
elif x_pubkey[0:2] == 'ff':
if not isinstance(self, BIP32_Wallet): return False
xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
return xpub in [ self.master_public_keys[k] for k in self.master_private_keys.keys() ]
elif x_pubkey[0:2] == 'fe':
if not isinstance(self, OldWallet): return False
xpub, sequence = OldAccount.parse_xpubkey(x_pubkey)
return xpub == self.get_master_public_key()
elif x_pubkey[0:2] == 'fd':
@ -1140,17 +1142,15 @@ class Deterministic_Wallet(Abstract_Wallet):
if value >= self.gap_limit:
self.gap_limit = value
self.storage.put('gap_limit', self.gap_limit, True)
#self.interface.poke('synchronizer')
return True
elif value >= self.min_acceptable_gap():
for key, account in self.accounts.items():
addresses = account[0]
addresses = account.get_addresses(False)
k = self.num_unused_trailing_addresses(addresses)
n = len(addresses) - k + value
addresses = addresses[0:n]
self.accounts[key][0] = addresses
account.receiving_pubkeys = account.receiving_pubkeys[0:n]
account.receiving_addresses = account.receiving_addresses[0:n]
self.gap_limit = value
self.storage.put('gap_limit', self.gap_limit, True)
self.save_accounts()
@ -1264,12 +1264,12 @@ class Deterministic_Wallet(Abstract_Wallet):
class BIP32_Wallet(Deterministic_Wallet):
# abstract class, bip32 logic
root_name = 'x/'
gap_limit = 20
def __init__(self, storage):
Deterministic_Wallet.__init__(self, storage)
self.master_public_keys = storage.get('master_public_keys', {})
self.master_private_keys = storage.get('master_private_keys', {})
self.gap_limit = storage.get('gap_limit', 20)
def is_watching_only(self):
return not bool(self.master_private_keys)
@ -1539,7 +1539,6 @@ class Wallet_2of3(Wallet_2of2):
class OldWallet(Deterministic_Wallet):
wallet_type = 'old'
gap_limit = 5
def __init__(self, storage):
Deterministic_Wallet.__init__(self, storage)
@ -1632,12 +1631,21 @@ class Wallet(object):
seed_version = OLD_SEED_VERSION if len(storage.get('master_public_key','')) == 128 else NEW_SEED_VERSION
if seed_version not in [OLD_SEED_VERSION, NEW_SEED_VERSION]:
msg = "This wallet seed is not supported anymore."
msg = "Your wallet has an unsupported seed version."
msg += '\n\nWallet file: %s' % os.path.abspath(storage.path)
if seed_version in [5, 7, 8, 9, 10]:
msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
msg += "\n\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
if seed_version == 6:
# version 1.9.8 created v6 wallets when an incorrect seed was entered in the restore dialog
msg += '\n\nThis file was created because of a bug in version 1.9.8.'
if storage.get('master_public_keys') is None and storage.get('master_private_keys') is None and storage.get('imported_keys') is None:
# pbkdf2 was not included with the binaries, and wallet creation aborted.
msg += "\nIt does not contain any keys, and can safely be removed."
else:
# creation was complete if electrum was run from source
msg += "\nPlease open this file with Electrum 1.9.8, and move your coins to a new wallet."
raise BaseException(msg)
run_hook('add_wallet_types', wallet_types)
wallet_type = storage.get('wallet_type')
if wallet_type:
for cat, t, name, c in wallet_types:

View File

@ -20,21 +20,9 @@
from datetime import datetime
import sys
try:
import pyasn1
except ImportError:
sys.exit("Error: pyasn1 does not seem to be installed. Try 'sudo pip install pyasn1'")
try:
import pyasn1_modules
except ImportError:
sys.exit("Error: pyasn1 does not seem to be installed. Try 'sudo pip install pyasn1-modules'")
try:
import tlslite
except ImportError:
sys.exit("Error: tlslite does not seem to be installed. Try 'sudo pip install tlslite'")
import pyasn1
import pyasn1_modules
import tlslite
# workaround https://github.com/trevp/tlslite/issues/15
tlslite.utils.cryptomath.pycryptoLoaded = False

View File

@ -106,10 +106,8 @@ class Plugin(BasePlugin):
button.clicked.connect(handler)
def _audio_interface(self):
return amodem.audio.Interface(
config=self.modem_config,
name=self.library_name
)
interface = amodem.audio.Interface(config=self.modem_config)
return interface.load(self.library_name)
def _send(self, parent, blob):
def sender_thread():

View File

@ -15,7 +15,7 @@ from electrum.bitcoin import EncodeBase58Check, DecodeBase58Check, public_key_to
from electrum.i18n import _
from electrum.plugins import BasePlugin, hook
from electrum.transaction import deserialize
from electrum.wallet import NewWallet
from electrum.wallet import BIP32_HD_Wallet
from electrum.util import format_satoshis
import hashlib
@ -45,19 +45,21 @@ class Plugin(BasePlugin):
def __init__(self, gui, name):
BasePlugin.__init__(self, gui, name)
self._is_available = self._init()
self.wallet = None
electrum.wallet.wallet_types.append(('hardware', 'btchip', _("BTChip wallet"), BTChipWallet))
self.wallet = None
if self._is_available:
electrum.wallet.wallet_types.append(('hardware', 'btchip', _("BTChip wallet"), BTChipWallet))
def _init(self):
return BTCHIP
def is_available(self):
if self.wallet is None:
return self._is_available
if self.wallet.storage.get('wallet_type') == 'btchip':
return True
return False
def is_available(self):
if not self._is_available:
return False
if not self.wallet:
return False
if self.wallet.storage.get('wallet_type') != 'btchip':
return False
return True
def set_enabled(self, enabled):
self.wallet.storage.put('use_' + self.name, enabled)
@ -65,19 +67,30 @@ class Plugin(BasePlugin):
def is_enabled(self):
if not self.is_available():
return False
if not self.wallet or self.wallet.storage.get('wallet_type') == 'btchip':
return True
return self.wallet.storage.get('use_' + self.name) is True
if self.wallet.has_seed():
return False
return True
def enable(self):
return BasePlugin.enable(self)
def btchip_is_connected(self):
try:
self.wallet.get_client().getFirmwareVersion()
except:
return False
return True
@hook
def load_wallet(self, wallet):
self.wallet = wallet
if self.btchip_is_connected():
if not self.wallet.check_proper_device():
QMessageBox.information(self.window, _('Error'), _("This wallet does not match your BTChip device"), _('OK'))
self.wallet.force_watching_only = True
else:
QMessageBox.information(self.window, _('Error'), _("BTChip device not detected.\nContinuing in watching-only mode."), _('OK'))
self.wallet.force_watching_only = True
@hook
def installwizard_restore(self, wizard, storage):
if storage.get('wallet_type') != 'btchip':
@ -98,16 +111,18 @@ class Plugin(BasePlugin):
except Exception as e:
tx.error = str(e)
class BTChipWallet(NewWallet):
class BTChipWallet(BIP32_HD_Wallet):
wallet_type = 'btchip'
root_derivation = "m/44'/0'"
def __init__(self, storage):
NewWallet.__init__(self, storage)
BIP32_HD_Wallet.__init__(self, storage)
self.transport = None
self.client = None
self.mpk = None
self.device_checked = False
self.signing = False
self.force_watching_only = False
def give_error(self, message, clear_client = False):
if not self.signing:
@ -129,11 +144,8 @@ class BTChipWallet(NewWallet):
def can_change_password(self):
return False
def has_seed(self):
return False
def is_watching_only(self):
return False
return self.force_watching_only
def get_client(self, noPin=False):
if not BTCHIP:
@ -258,9 +270,6 @@ class BTChipWallet(NewWallet):
def get_master_public_key(self):
try:
if not self.mpk:
self.get_client() # prompt for the PIN if necessary
if not self.check_proper_device():
self.give_error('Wrong device or password')
self.mpk = self.get_public_key("44'/0'")
return self.mpk
except Exception, e:
@ -278,6 +287,7 @@ class BTChipWallet(NewWallet):
def sign_message(self, address, message, password):
use2FA = False
self.signing = True
self.get_client() # prompt for the PIN before displaying the dialog if necessary
if not self.check_proper_device():
self.give_error('Wrong device or password')
@ -298,11 +308,15 @@ class BTChipWallet(NewWallet):
self.get_client(True)
signature = self.get_client().signMessageSign(pin)
except Exception, e:
self.give_error(e, True)
if e.sw == 0x6a80:
self.give_error("Unfortunately, this message cannot be signed by BTChip. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry.")
else:
self.give_error(e, True)
finally:
if waitDialog.waiting:
waitDialog.emit(SIGNAL('dongle_done'))
self.client.bad = use2FA
self.signing = False
# Parse the ASN.1 signature

View File

@ -93,14 +93,9 @@ class Plugin(BasePlugin):
def init_qt(self, gui):
self.win = gui.main_window
self.win.connect(self.win, SIGNAL('cosigner:receive'), self.on_receive)
if self.listener is None:
self.listener = Listener(self)
self.listener.start()
def enable(self):
self.set_enabled(True)
if self.win.wallet:
self.load_wallet(self.win.wallet)
return True
def is_available(self):
@ -113,6 +108,9 @@ class Plugin(BasePlugin):
self.wallet = wallet
if not self.is_available():
return
if self.listener is None:
self.listener = Listener(self)
self.listener.start()
mpk = self.wallet.get_master_public_keys()
self.cosigner_list = []
for key, xpub in mpk.items():

View File

@ -49,17 +49,20 @@ class Plugin(BasePlugin):
self._is_available = self._init()
self._requires_settings = True
self.wallet = None
electrum.wallet.wallet_types.append(('hardware', 'trezor', _("Trezor wallet"), TrezorWallet))
if self._is_available:
electrum.wallet.wallet_types.append(('hardware', 'trezor', _("Trezor wallet"), TrezorWallet))
def _init(self):
return TREZOR
def is_available(self):
if self.wallet is None:
return self._is_available
if self.wallet.storage.get('wallet_type') == 'trezor':
return True
return False
if not self._is_available:
return False
if not self.wallet:
return False
if self.wallet.storage.get('wallet_type') != 'trezor':
return False
return True
def requires_settings(self):
return self._requires_settings
@ -70,11 +73,9 @@ class Plugin(BasePlugin):
def is_enabled(self):
if not self.is_available():
return False
if not self.wallet or self.wallet.storage.get('wallet_type') == 'trezor':
return True
return self.wallet.storage.get('use_' + self.name) is True
if self.wallet.has_seed():
return False
return True
def enable(self):
return BasePlugin.enable(self)
@ -93,28 +94,34 @@ class Plugin(BasePlugin):
@hook
def close_wallet(self):
print_error("trezor: clear session")
if self.wallet.client:
if self.wallet and self.wallet.client:
self.wallet.client.clear_session()
@hook
def load_wallet(self, wallet):
self.wallet = wallet
if self.trezor_is_connected():
if not self.wallet.check_proper_device():
QMessageBox.information(self.window, _('Error'), _("This wallet does not match your Trezor device"), _('OK'))
self.wallet.force_watching_only = True
else:
QMessageBox.information(self.window, _('Error'), _("Trezor device not detected.\nContinuing in watching-only mode."), _('OK'))
self.wallet.force_watching_only = True
@hook
def installwizard_restore(self, wizard, storage):
if storage.get('wallet_type') != 'trezor':
return
wallet = TrezorWallet(storage)
try:
wallet.create_main_account(None)
except BaseException as e:
QMessageBox.information(None, _('Error'), str(e), _('OK'))
seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
if not seed:
return
wallet = TrezorWallet(storage)
self.wallet = wallet
password = wizard.password_dialog()
wallet.add_seed(seed, password)
wallet.add_cosigner_seed(' '.join(seed.split()), 'x/', password)
wallet.create_main_account(password)
# disable trezor plugin
self.set_enabled(False)
return wallet
@hook
@ -161,6 +168,8 @@ class Plugin(BasePlugin):
return False
from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root
class TrezorWallet(BIP32_HD_Wallet):
wallet_type = 'trezor'
root_derivation = "m/44'/0'"
@ -171,6 +180,7 @@ class TrezorWallet(BIP32_HD_Wallet):
self.client = None
self.mpk = None
self.device_checked = False
self.force_watching_only = False
def get_action(self):
if not self.accounts:
@ -188,11 +198,8 @@ class TrezorWallet(BIP32_HD_Wallet):
def can_change_password(self):
return False
def has_seed(self):
return False
def is_watching_only(self):
return False
return self.force_watching_only
def get_client(self):
if not TREZOR:
@ -221,10 +228,23 @@ class TrezorWallet(BIP32_HD_Wallet):
def create_main_account(self, password):
self.create_account('Main account', None) #name, empty password
def mnemonic_to_seed(self, mnemonic, passphrase):
# trezor uses bip39
import pbkdf2, hashlib, hmac
PBKDF2_ROUNDS = 2048
mnemonic = ' '.join(mnemonic.split())
return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
def derive_xkeys(self, root, derivation, password):
derivation = derivation.replace(self.root_name,"44'/0'/")
xpub = self.get_public_key(derivation)
return xpub, None
x = self.master_private_keys.get(root)
if x:
root_xprv = pw_decode(x, password)
xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
return xpub, xprv
else:
derivation = derivation.replace(self.root_name,"44'/0'/")
xpub = self.get_public_key(derivation)
return xpub, None
def get_public_key(self, bip32_path):
address_n = self.get_client().expand_path(bip32_path)

View File

@ -223,8 +223,8 @@ class Plugin(BasePlugin):
+ _("For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
def is_available(self):
if self.wallet is None:
return True
if not self.wallet:
return False
if self.wallet.storage.get('wallet_type') == '2fa':
return True
return False
@ -238,10 +238,6 @@ class Plugin(BasePlugin):
def is_enabled(self):
if not self.is_available():
return False
if not self.wallet:
return True
if self.wallet.storage.get('wallet_type') != '2fa':
return False
if self.wallet.master_private_keys.get('x2/'):
return False
return True
@ -344,15 +340,13 @@ class Plugin(BasePlugin):
@hook
def load_wallet(self, wallet):
self.wallet = wallet
if self.is_enabled():
self.trustedcoin_button = StatusBarButton( QIcon(":icons/trustedcoin.png"), _("Network"), self.settings_dialog)
self.window.statusBar().addPermanentWidget(self.trustedcoin_button)
self.xpub = self.wallet.master_public_keys.get('x1/')
self.user_id = self.get_user_id()[1]
t = threading.Thread(target=self.request_billing_info)
t.setDaemon(True)
t.start()
self.trustedcoin_button = StatusBarButton( QIcon(":icons/trustedcoin.png"), _("Network"), self.settings_dialog)
self.window.statusBar().addPermanentWidget(self.trustedcoin_button)
self.xpub = self.wallet.master_public_keys.get('x1/')
self.user_id = self.get_user_id()[1]
t = threading.Thread(target=self.request_billing_info)
t.setDaemon(True)
t.start()
@hook
def close_wallet(self):
@ -481,6 +475,7 @@ class Plugin(BasePlugin):
return 0
# trustedcoin won't charge if the total inputs is lower than their fee
price = int(self.price_per_tx.get(1))
assert price <= 100000
if tx.input_value() < price:
print_error("not charging for this tx")
return 0

View File

@ -37,7 +37,7 @@ if sys.platform == 'darwin':
app=[mainscript],
options=dict(py2app=dict(argv_emulation=True,
includes=['PyQt4.QtCore', 'PyQt4.QtGui', 'PyQt4.QtWebKit', 'PyQt4.QtNetwork', 'sip'],
packages=['lib', 'gui', 'plugins'],
packages=['lib', 'gui', 'plugins', 'packages'],
iconfile='electrum.icns',
plist=plist,
resources=["data", "icons"])),

View File

@ -65,14 +65,15 @@ setup(
name="Electrum",
version=version.ELECTRUM_VERSION,
install_requires=[
'slowaes',
'slowaes>=0.1a1',
'ecdsa>=0.9',
'pbkdf2',
'requests',
'pyasn1',
'pyasn1-modules',
'pyasn1',
'qrcode',
'SocksiPy-branch',
'protobuf',
'tlslite',
'dnspython'
],