Fix merge conflict with master
This commit is contained in:
commit
23d314462f
1
TODO
1
TODO
|
@ -3,6 +3,7 @@ Client:
|
|||
- Wizard
|
||||
- Multiple wallets
|
||||
- Themes
|
||||
- Extend GUI with history view (View -> Show History)
|
||||
- Settings window
|
||||
|
||||
Server:
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="opacity:1;fill:#d7d7d7;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
style="opacity:0.83716047999999998;fill:#d7d7d7;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 472.75139,908.92052 c 46.46702,-26.26397 56.56855,-68.69038 66.67007,-98.99495 10.10153,-30.30458 52.52793,-228.29448 42.42641,-347.49248 -10.10153,-119.198 -58.58885,-135.36044 -107.07617,-159.6041 -48.48732,-24.24366 -133.34014,-18.18275 -175.76654,8.08122 -42.42642,26.26397 -92.93405,60.60915 -101.01527,143.44166 -8.08122,82.83251 9.13377,276.70605 46.46702,371.73614 22.22336,56.56854 47.29278,70.95769 78.79191,84.85281 31.49913,13.89512 116.60088,16.01267 149.50257,-2.0203 z"
|
||||
id="path3050"
|
||||
inkscape:connector-curvature="0"
|
||||
|
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.7 KiB |
60
electrum
60
electrum
|
@ -16,7 +16,11 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re, sys
|
||||
import re
|
||||
import sys
|
||||
# import argparse
|
||||
import optparse
|
||||
|
||||
try:
|
||||
from lib.util import print_error
|
||||
except ImportError:
|
||||
|
@ -24,22 +28,19 @@ except ImportError:
|
|||
|
||||
try:
|
||||
import ecdsa
|
||||
except:
|
||||
print_error("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
|
||||
sys.exit(1)
|
||||
except ImportError:
|
||||
sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
|
||||
|
||||
try:
|
||||
import aes
|
||||
except:
|
||||
print_error("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
|
||||
sys.exit(1)
|
||||
except ImportError:
|
||||
sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
|
||||
|
||||
try:
|
||||
from lib import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password
|
||||
except ImportError:
|
||||
from electrum import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password
|
||||
|
||||
from optparse import OptionParser
|
||||
from decimal import Decimal
|
||||
|
||||
known_commands = {
|
||||
|
@ -77,9 +78,9 @@ options:\n --fee, -f: set transaction fee\n --fromaddr, -s: send from address
|
|||
'import':
|
||||
'Imports a key pair\nSyntax: import <address>:<privatekey>',
|
||||
'signmessage':
|
||||
'Signs a message with a key\nSyntax: signmessage <address> <message>',
|
||||
'Signs a message with a key\nSyntax: signmessage <address> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello This is a weird String "',
|
||||
'verifymessage':
|
||||
'Verifies a signature\nSyntax: verifymessage <address> <signature> <message>',
|
||||
'Verifies a signature\nSyntax: verifymessage <address> <signature> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello This is a weird String "',
|
||||
'eval':
|
||||
"Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"",
|
||||
'deseed':
|
||||
|
@ -101,7 +102,7 @@ protected_commands = ['payto', 'password', 'mktx', 'seed', 'import','signmessage
|
|||
if __name__ == '__main__':
|
||||
|
||||
usage = "usage: %prog [options] command\nCommands: "+ (', '.join(known_commands))
|
||||
parser = OptionParser(usage=usage)
|
||||
parser = optparse.OptionParser(prog=usage)
|
||||
parser.add_option("-g", "--gui", dest="gui", default="lite", help="gui")
|
||||
parser.add_option("-w", "--wallet", dest="wallet_path", help="wallet path (default: electrum.dat)")
|
||||
parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
|
||||
|
@ -129,6 +130,8 @@ if __name__ == '__main__':
|
|||
cmd = args[0]
|
||||
firstarg = args[1] if len(args) > 1 else ''
|
||||
|
||||
#this entire if/else block is just concerned with importing the
|
||||
#right GUI toolkit based the GUI command line option given
|
||||
if cmd == 'gui':
|
||||
|
||||
if options.gui=='gtk':
|
||||
|
@ -165,16 +168,17 @@ if __name__ == '__main__':
|
|||
except ImportError:
|
||||
import electrum.gui_qt as gui
|
||||
else:
|
||||
#use the lite version if no toolkit specified
|
||||
try:
|
||||
import lib.gui_lite as gui
|
||||
except ImportError:
|
||||
import electrum.gui_lite as gui
|
||||
else:
|
||||
print_error("Error: Unknown GUI: " + options.gui)
|
||||
exit(1)
|
||||
sys.exit("Error: Unknown GUI: " + options.gui)
|
||||
|
||||
gui = gui.ElectrumGui(wallet)
|
||||
WalletSynchronizer(wallet,True).start()
|
||||
interface = WalletSynchronizer(wallet, True, gui.server_list_changed)
|
||||
interface.start()
|
||||
|
||||
try:
|
||||
found = wallet.file_exists
|
||||
|
@ -198,15 +202,13 @@ if __name__ == '__main__':
|
|||
cmd = 'help'
|
||||
|
||||
if not wallet.file_exists and cmd not in ['help','create','restore']:
|
||||
print_error("Error: Wallet file not found.")
|
||||
print_error("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
|
||||
print "Error: Wallet file not found."
|
||||
print "Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option"
|
||||
sys.exit(0)
|
||||
|
||||
if cmd in ['create', 'restore']:
|
||||
if wallet.file_exists:
|
||||
print_error("Error: Remove the existing wallet first!")
|
||||
sys.stderr.flush()
|
||||
sys.exit(0)
|
||||
sys.exit("Error: Remove the existing wallet first!")
|
||||
password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
|
||||
|
||||
w_host, w_port, w_protocol = wallet.server.split(':')
|
||||
|
@ -230,8 +232,7 @@ if __name__ == '__main__':
|
|||
print_error("Warning: Not hex, trying decode.")
|
||||
seed = mnemonic.mn_decode( seed.split(' ') )
|
||||
if not seed:
|
||||
print_error("Error: No seed")
|
||||
sys.exit(1)
|
||||
sys.exit("Error: No seed")
|
||||
|
||||
wallet.seed = str(seed)
|
||||
wallet.init_mpk( wallet.seed )
|
||||
|
@ -316,7 +317,6 @@ if __name__ == '__main__':
|
|||
cmd2 = firstarg
|
||||
if cmd2 not in known_commands:
|
||||
parser.print_help()
|
||||
print
|
||||
print "Type 'electrum help <command>' to see the help for a specific command"
|
||||
print "Type 'electrum --help' to see the list of options"
|
||||
print "List of commands:", ', '.join(known_commands)
|
||||
|
@ -355,17 +355,15 @@ if __name__ == '__main__':
|
|||
f = open(ns,'r')
|
||||
data = f.read()
|
||||
f.close()
|
||||
except:
|
||||
print_error("Error: Seed file not found")
|
||||
sys.exit()
|
||||
except IOError:
|
||||
sys.exit("Error: Seed file not found")
|
||||
try:
|
||||
import ast
|
||||
d = ast.literal_eval( data )
|
||||
seed = d['seed']
|
||||
imported_keys = d.get('imported_keys',{})
|
||||
except:
|
||||
print_error("Error: Error with seed file")
|
||||
sys.exit(1)
|
||||
sys.exit("Error: Error with seed file")
|
||||
|
||||
mpk = wallet.master_public_key
|
||||
wallet.seed = seed
|
||||
|
@ -511,9 +509,8 @@ if __name__ == '__main__':
|
|||
elif cmd == 'password':
|
||||
try:
|
||||
seed = wallet.pw_decode( wallet.seed, password)
|
||||
except:
|
||||
print_error("Error: Password does not decrypt this wallet.")
|
||||
sys.exit(1)
|
||||
except ValueError:
|
||||
sys.exit("Error: Password does not decrypt this wallet.")
|
||||
|
||||
new_password = prompt_password('New password:')
|
||||
wallet.update_password(seed, password, new_password)
|
||||
|
@ -543,7 +540,8 @@ if __name__ == '__main__':
|
|||
try:
|
||||
wallet.verify_message(address, signature, message)
|
||||
print True
|
||||
except:
|
||||
except BaseException as e:
|
||||
print "Verification error: {0}".format(e)
|
||||
print False
|
||||
|
||||
elif cmd == 'freeze':
|
||||
|
|
BIN
electrum.png
BIN
electrum.png
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 8.7 KiB |
|
@ -31,7 +31,7 @@ class Exchanger(threading.Thread):
|
|||
connection = httplib.HTTPSConnection('intersango.com')
|
||||
connection.request("GET", "/api/ticker.php")
|
||||
response = connection.getresponse()
|
||||
if response.status == 404:
|
||||
if response.reason == httplib.responses[httplib.NOT_FOUND]:
|
||||
return
|
||||
response = json.loads(response.read())
|
||||
# 1 = BTC:GBP
|
||||
|
@ -40,16 +40,16 @@ class Exchanger(threading.Thread):
|
|||
# 4 = BTC:PLN
|
||||
quote_currencies = {}
|
||||
try:
|
||||
quote_currencies["GBP"] = self.lookup_rate(response, 1)
|
||||
quote_currencies["EUR"] = self.lookup_rate(response, 2)
|
||||
quote_currencies["USD"] = self.lookup_rate(response, 3)
|
||||
quote_currencies["GBP"] = self._lookup_rate(response, 1)
|
||||
quote_currencies["EUR"] = self._lookup_rate(response, 2)
|
||||
quote_currencies["USD"] = self._lookup_rate(response, 3)
|
||||
with self.lock:
|
||||
self.quote_currencies = quote_currencies
|
||||
self.parent.emit(SIGNAL("refresh_balance()"))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def lookup_rate(self, response, quote_id):
|
||||
def _lookup_rate(self, response, quote_id):
|
||||
return decimal.Decimal(response[str(quote_id)]["last"])
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
122
lib/gui_lite.py
122
lib/gui_lite.py
|
@ -4,6 +4,7 @@ from PyQt4.QtCore import *
|
|||
from PyQt4.QtGui import *
|
||||
|
||||
from decimal import Decimal as D
|
||||
from interface import DEFAULT_SERVERS
|
||||
from util import get_resource_path as rsrc
|
||||
from i18n import _
|
||||
import decimal
|
||||
|
@ -41,14 +42,18 @@ def resize_line_edit_width(line_edit, text_input):
|
|||
text_input += "A"
|
||||
line_edit.setMinimumWidth(metrics.width(text_input))
|
||||
|
||||
class ElectrumGui:
|
||||
class ElectrumGui(QObject):
|
||||
|
||||
def __init__(self, wallet):
|
||||
super(QObject, self).__init__()
|
||||
|
||||
self.wallet = wallet
|
||||
self.app = QApplication(sys.argv)
|
||||
|
||||
def main(self, url):
|
||||
actuator = MiniActuator(self.wallet)
|
||||
self.connect(self, SIGNAL("updateservers()"),
|
||||
actuator.update_servers_list)
|
||||
# Should probably not modify the current path but instead
|
||||
# change the behaviour of rsrc(...)
|
||||
old_path = QDir.currentPath()
|
||||
|
@ -73,7 +78,11 @@ class ElectrumGui:
|
|||
|
||||
self.app.exec_()
|
||||
|
||||
def server_list_changed(self):
|
||||
self.emit(SIGNAL("updateservers()"))
|
||||
|
||||
def expand(self):
|
||||
"""Hide the lite mode window and show pro-mode."""
|
||||
self.mini.hide()
|
||||
self.expert.show()
|
||||
|
||||
|
@ -174,13 +183,15 @@ class MiniWindow(QDialog):
|
|||
|
||||
menubar = QMenuBar()
|
||||
electrum_menu = menubar.addMenu(_("&Bitcoin"))
|
||||
self.servers_menu = electrum_menu.addMenu(_("&Servers"))
|
||||
self.servers_menu.addAction(_("Foo"))
|
||||
servers_menu = electrum_menu.addMenu(_("&Servers"))
|
||||
servers_group = QActionGroup(self)
|
||||
self.actuator.set_servers_gui_stuff(servers_menu, servers_group)
|
||||
self.actuator.populate_servers_menu()
|
||||
electrum_menu.addSeparator()
|
||||
brain_seed = electrum_menu.addAction(_("&BrainWallet Info"))
|
||||
brain_seed.triggered.connect(self.actuator.show_seed_dialog)
|
||||
|
||||
electrum_menu.addAction(_("&Quit"))
|
||||
quit_option = electrum_menu.addAction(_("&Quit"))
|
||||
quit_option.triggered.connect(self.close)
|
||||
|
||||
view_menu = menubar.addMenu(_("&View"))
|
||||
expert_gui = view_menu.addAction(_("&Pro Mode"))
|
||||
|
@ -254,9 +265,10 @@ class MiniWindow(QDialog):
|
|||
pass
|
||||
|
||||
def set_quote_currency(self, currency):
|
||||
"""Set and display the fiat currency country."""
|
||||
assert currency in self.quote_currencies
|
||||
self.quote_currencies.remove(currency)
|
||||
self.quote_currencies = [currency] + self.quote_currencies
|
||||
self.quote_currencies.insert(0, currency)
|
||||
self.refresh_balance()
|
||||
|
||||
def change_quote_currency(self):
|
||||
|
@ -274,6 +286,7 @@ class MiniWindow(QDialog):
|
|||
self.amount_input_changed(self.amount_input.text())
|
||||
|
||||
def set_balances(self, btc_balance):
|
||||
"""Set the bitcoin balance and update the amount label accordingly."""
|
||||
self.btc_balance = btc_balance
|
||||
quote_text = self.create_quote_text(btc_balance)
|
||||
if quote_text:
|
||||
|
@ -283,6 +296,7 @@ class MiniWindow(QDialog):
|
|||
self.setWindowTitle("Electrum - %s BTC" % btc_balance)
|
||||
|
||||
def amount_input_changed(self, amount_text):
|
||||
"""Update the number of bitcoins displayed."""
|
||||
self.check_button_status()
|
||||
|
||||
try:
|
||||
|
@ -298,6 +312,8 @@ class MiniWindow(QDialog):
|
|||
self.balance_label.show_balance()
|
||||
|
||||
def create_quote_text(self, btc_balance):
|
||||
"""Return a string copy of the amount fiat currency the
|
||||
user has in bitcoins."""
|
||||
quote_currency = self.quote_currencies[0]
|
||||
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
|
||||
if quote_balance is None:
|
||||
|
@ -314,8 +330,16 @@ class MiniWindow(QDialog):
|
|||
self.amount_input.setText("")
|
||||
|
||||
def check_button_status(self):
|
||||
"""Check that the bitcoin address is valid and that something
|
||||
is entered in the amount before making the send button clickable."""
|
||||
try:
|
||||
value = D(str(self.amount_input.text())) * 10**8
|
||||
except decimal.InvalidOperation:
|
||||
value = None
|
||||
# self.address_input.property(...) returns a qVariant, not a bool.
|
||||
# The == is needed to properly invoke a comparison.
|
||||
if (self.address_input.property("isValid") == True and
|
||||
len(self.amount_input.text()) > 0):
|
||||
value is not None and 0 < value <= self.btc_balance):
|
||||
self.send_button.setDisabled(False)
|
||||
else:
|
||||
self.send_button.setDisabled(True)
|
||||
|
@ -385,10 +409,12 @@ class BalanceLabel(QLabel):
|
|||
self.amount_text = ""
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Change the fiat currency selection if window background is clicked."""
|
||||
if self.state != self.SHOW_CONNECTING:
|
||||
self.change_quote_currency()
|
||||
|
||||
def set_balance_text(self, btc_balance, quote_text):
|
||||
"""Set the amount of bitcoins in the gui."""
|
||||
if self.state == self.SHOW_CONNECTING:
|
||||
self.state = self.SHOW_BALANCE
|
||||
self.balance_text = "<span style='font-size: 18pt'>%s</span> <span style='font-size: 10pt'>BTC</span> <span style='font-size: 10pt'>%s</span>" % (btc_balance, quote_text)
|
||||
|
@ -466,7 +492,8 @@ class ReceivePopup(QDialog):
|
|||
|
||||
self.setMouseTracking(True)
|
||||
self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
|
||||
self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|Qt.MSWindowsFixedSizeDialogHint)
|
||||
self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
|
||||
Qt.MSWindowsFixedSizeDialogHint)
|
||||
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
||||
#self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
|
||||
#self.setAlignment(Qt.AlignCenter)
|
||||
|
@ -480,33 +507,43 @@ class ReceivePopup(QDialog):
|
|||
self.show()
|
||||
|
||||
class MiniActuator:
|
||||
"""Initialize the definitions relating to themes and
|
||||
sending/recieving bitcoins."""
|
||||
|
||||
|
||||
def __init__(self, wallet):
|
||||
"""Retrieve the gui theme used in previous session."""
|
||||
self.wallet = wallet
|
||||
|
||||
self.theme_name = self.wallet.theme
|
||||
self.themes = util.load_theme_paths()
|
||||
|
||||
def load_theme(self):
|
||||
"""Load theme retrieved from wallet file."""
|
||||
try:
|
||||
theme_prefix, theme_path = self.themes[self.theme_name]
|
||||
except KeyError:
|
||||
util.print_error("Theme not found! ", self.theme_name)
|
||||
util.print_error("Theme not found!", self.theme_name)
|
||||
return
|
||||
QDir.setCurrent(os.path.join(theme_prefix, theme_path))
|
||||
with open(rsrc("style.css")) as style_file:
|
||||
qApp.setStyleSheet(style_file.read())
|
||||
|
||||
def theme_names(self):
|
||||
return self.themes.keys()
|
||||
"""Sort themes."""
|
||||
return sorted(self.themes.keys())
|
||||
|
||||
def selected_theme(self):
|
||||
"""Select theme."""
|
||||
return self.theme_name
|
||||
|
||||
def change_theme(self, theme_name):
|
||||
"""Change theme."""
|
||||
self.wallet.theme = self.theme_name = theme_name
|
||||
self.load_theme()
|
||||
|
||||
def set_configured_currency(self, set_quote_currency):
|
||||
"""Set the inital fiat currency conversion country (USD/EUR/GBP) in
|
||||
the GUI to what it was set to in the wallet."""
|
||||
currency = self.wallet.conversion_currency
|
||||
# currency can be none when Electrum is used for the first
|
||||
# time and no setting has been created yet.
|
||||
|
@ -514,9 +551,70 @@ class MiniActuator:
|
|||
set_quote_currency(currency)
|
||||
|
||||
def set_config_currency(self, conversion_currency):
|
||||
"""Change the wallet fiat currency country."""
|
||||
self.wallet.conversion_currency = conversion_currency
|
||||
|
||||
def set_servers_gui_stuff(self, servers_menu, servers_group):
|
||||
self.servers_menu = servers_menu
|
||||
self.servers_group = servers_group
|
||||
|
||||
def populate_servers_menu(self):
|
||||
interface = self.wallet.interface
|
||||
if not interface.servers:
|
||||
print "No servers loaded yet."
|
||||
self.servers_list = []
|
||||
for server_string in DEFAULT_SERVERS:
|
||||
host, port, protocol = server_string.split(':')
|
||||
transports = [(protocol,port)]
|
||||
self.servers_list.append((host, transports))
|
||||
else:
|
||||
print "Servers loaded."
|
||||
self.servers_list = interface.servers
|
||||
server_names = [details[0] for details in self.servers_list]
|
||||
current_server = self.wallet.server.split(":")[0]
|
||||
for server_name in server_names:
|
||||
server_action = self.servers_menu.addAction(server_name)
|
||||
server_action.setCheckable(True)
|
||||
if server_name == current_server:
|
||||
server_action.setChecked(True)
|
||||
class SelectServerFunctor:
|
||||
def __init__(self, server_name, server_selected):
|
||||
self.server_name = server_name
|
||||
self.server_selected = server_selected
|
||||
def __call__(self, checked):
|
||||
if checked:
|
||||
# call server_selected
|
||||
self.server_selected(self.server_name)
|
||||
delegate = SelectServerFunctor(server_name, self.server_selected)
|
||||
server_action.toggled.connect(delegate)
|
||||
self.servers_group.addAction(server_action)
|
||||
|
||||
def update_servers_list(self):
|
||||
# Clear servers_group
|
||||
for action in self.servers_group.actions():
|
||||
self.servers_group.removeAction(action)
|
||||
self.populate_servers_menu()
|
||||
|
||||
def server_selected(self, server_name):
|
||||
match = [transports for (host, transports) in self.servers_list
|
||||
if host == server_name]
|
||||
assert len(match) == 1
|
||||
match = match[0]
|
||||
# Default to TCP if available else use anything
|
||||
# TODO: protocol should be selectable.
|
||||
tcp_port = [port for (protocol, port) in match if protocol == "t"]
|
||||
if len(tcp_port) == 0:
|
||||
protocol = match[0][0]
|
||||
port = match[0][1]
|
||||
else:
|
||||
protocol = "t"
|
||||
port = tcp_port[0]
|
||||
server_line = "%s:%s:%s" % (server_name, port, protocol)
|
||||
# Should this have exception handling?
|
||||
self.wallet.set_server(server_line)
|
||||
|
||||
def copy_address(self, receive_popup):
|
||||
"""Copy the wallet addresses into the client."""
|
||||
addrs = [addr for addr in self.wallet.all_addresses()
|
||||
if not self.wallet.is_change(addr)]
|
||||
# Select most recent addresses from gap limit
|
||||
|
@ -527,6 +625,7 @@ class MiniActuator:
|
|||
receive_popup.popup()
|
||||
|
||||
def send(self, address, amount, parent_window):
|
||||
"""Send bitcoins to the target address."""
|
||||
dest_address = self.fetch_destination(address)
|
||||
|
||||
if dest_address is None or not self.wallet.is_valid(dest_address):
|
||||
|
@ -589,6 +688,7 @@ class MiniActuator:
|
|||
return recipient
|
||||
|
||||
def is_valid(self, address):
|
||||
"""Check if bitcoin address is valid."""
|
||||
return self.wallet.is_valid(address)
|
||||
|
||||
def acceptbit(self, currency):
|
||||
|
|
|
@ -23,9 +23,7 @@ from util import print_error
|
|||
try:
|
||||
import PyQt4
|
||||
except:
|
||||
print_error("Error: Could not import PyQt4")
|
||||
print_error("on Linux systems, you may try 'sudo apt-get install python-qt4'")
|
||||
sys.exit(1)
|
||||
sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
|
@ -36,9 +34,7 @@ from interface import DEFAULT_SERVERS
|
|||
try:
|
||||
import icons_rc
|
||||
except:
|
||||
print_error("Error: Could not import icons_rc.py")
|
||||
print_error("Please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
|
||||
sys.exit(1)
|
||||
sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
|
||||
|
||||
from wallet import format_satoshis
|
||||
import bmp, mnemonic, pyqrnative, qrscanner
|
||||
|
@ -1434,6 +1430,9 @@ class ElectrumGui:
|
|||
if app is None:
|
||||
self.app = QApplication(sys.argv)
|
||||
|
||||
def server_list_changed(self):
|
||||
pass
|
||||
|
||||
def waiting_dialog(self):
|
||||
|
||||
s = Timer()
|
||||
|
|
|
@ -27,7 +27,7 @@ DEFAULT_TIMEOUT = 5
|
|||
DEFAULT_SERVERS = [ 'ecdsa.org:50001:t',
|
||||
'electrum.novit.ro:50001:t',
|
||||
'uncle-enzo.info:50001:t',
|
||||
'electrum.bytesized-hosting.com:50000:t'] # list of default servers
|
||||
'electrum.bytesized-hosting.com:50001:t'] # list of default servers
|
||||
|
||||
|
||||
def replace_keys(obj, old_key, new_key):
|
||||
|
@ -306,12 +306,13 @@ class TcpStratumInterface(Interface):
|
|||
|
||||
class WalletSynchronizer(threading.Thread):
|
||||
|
||||
def __init__(self, wallet, loop=False):
|
||||
def __init__(self, wallet, loop=False, servers_loaded_callback=None):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.wallet = wallet
|
||||
self.loop = loop
|
||||
self.init_interface()
|
||||
self.servers_loaded_callback = servers_loaded_callback
|
||||
|
||||
def init_interface(self):
|
||||
try:
|
||||
|
@ -334,7 +335,6 @@ class WalletSynchronizer(threading.Thread):
|
|||
self.interface = InterfaceClass(host, port, self.wallet.debug_server)
|
||||
self.wallet.interface = self.interface
|
||||
|
||||
|
||||
def handle_response(self, r):
|
||||
if r is None:
|
||||
return
|
||||
|
@ -354,15 +354,19 @@ class WalletSynchronizer(threading.Thread):
|
|||
host = item[1]
|
||||
ports = []
|
||||
version = None
|
||||
if len(item)>2:
|
||||
if len(item) > 2:
|
||||
for v in item[2]:
|
||||
if re.match("[th]\d+",v):
|
||||
ports.append((v[0],v[1:]))
|
||||
if re.match("v(.?)+",v):
|
||||
if re.match("[th]\d+", v):
|
||||
ports.append((v[0], v[1:]))
|
||||
if re.match("v(.?)+", v):
|
||||
version = v[1:]
|
||||
if ports and version:
|
||||
servers.append( (host, ports) )
|
||||
servers.append((host, ports))
|
||||
self.interface.servers = servers
|
||||
# servers_loaded_callback is None for commands, but should
|
||||
# NEVER be None when using the GUI.
|
||||
if self.servers_loaded_callback is not None:
|
||||
self.servers_loaded_callback()
|
||||
|
||||
elif method == 'blockchain.address.subscribe':
|
||||
addr = params[0]
|
||||
|
@ -425,6 +429,7 @@ class WalletSynchronizer(threading.Thread):
|
|||
self.wallet.trigger_callbacks()
|
||||
if self.loop:
|
||||
time.sleep(5)
|
||||
# Server has been changed. Copy callback for new interface.
|
||||
self.init_interface()
|
||||
self.start_interface()
|
||||
continue
|
||||
|
|
|
@ -3,12 +3,13 @@ import platform
|
|||
import sys
|
||||
|
||||
def print_error(*args):
|
||||
for item in args:
|
||||
sys.stderr.write(str(item))
|
||||
sys.stderr.write("\n")
|
||||
# Stringify args
|
||||
args = [str(item) for item in args]
|
||||
sys.stderr.write(" ".join(args) + "\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
def appdata_dir():
|
||||
"""Find the path to the application data directory; add an electrum folder and return path."""
|
||||
if platform.system() == "Windows":
|
||||
return os.path.join(os.environ["APPDATA"], "Electrum")
|
||||
elif platform.system() == "Linux":
|
||||
|
@ -23,6 +24,7 @@ def get_resource_path(*args):
|
|||
return os.path.join(".", *args)
|
||||
|
||||
def local_data_dir():
|
||||
"""Return path to the data folder."""
|
||||
assert sys.argv
|
||||
prefix_path = os.path.dirname(sys.argv[0])
|
||||
local_data = os.path.join(prefix_path, "data")
|
||||
|
|
|
@ -16,9 +16,20 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import base64
|
||||
import os
|
||||
import re
|
||||
import hashlib
|
||||
import copy
|
||||
import operator
|
||||
import ast
|
||||
import threading
|
||||
import random
|
||||
import getpass
|
||||
import aes
|
||||
import ecdsa
|
||||
|
||||
import sys, base64, os, re, hashlib, copy, operator, ast, threading, random, getpass
|
||||
import aes, ecdsa
|
||||
from ecdsa.util import string_to_number, number_to_string
|
||||
from util import print_error
|
||||
|
||||
|
@ -65,8 +76,7 @@ __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|||
__b58base = len(__b58chars)
|
||||
|
||||
def b58encode(v):
|
||||
""" encode v, which is a string of bytes, to base58.
|
||||
"""
|
||||
""" encode v, which is a string of bytes, to base58."""
|
||||
|
||||
long_value = 0L
|
||||
for (i, c) in enumerate(v[::-1]):
|
||||
|
@ -89,8 +99,7 @@ def b58encode(v):
|
|||
return (__b58chars[0]*nPad) + result
|
||||
|
||||
def b58decode(v, length):
|
||||
""" decode v into a string of len bytes
|
||||
"""
|
||||
""" decode v into a string of len bytes."""
|
||||
long_value = 0L
|
||||
for (i, c) in enumerate(v[::-1]):
|
||||
long_value += __b58chars.find(c) * (__b58base**i)
|
||||
|
@ -157,8 +166,7 @@ def prompt_password(prompt, confirm=True):
|
|||
password2 = getpass.getpass("Confirm: ")
|
||||
|
||||
if password != password2:
|
||||
print_error("Error: Passwords do not match.")
|
||||
sys.exit(1)
|
||||
sys.exit("Error: Passwords do not match.")
|
||||
|
||||
else:
|
||||
password = raw_input(prompt)
|
||||
|
@ -282,7 +290,7 @@ class Wallet:
|
|||
self.num_zeros = 0
|
||||
self.master_public_key = ''
|
||||
self.conversion_currency = None
|
||||
self.theme = None
|
||||
self.theme = "Cleanlook"
|
||||
|
||||
# saved fields
|
||||
self.use_encryption = False
|
||||
|
@ -342,7 +350,7 @@ class Wallet:
|
|||
# raise an error if the format isnt correct
|
||||
a,b,c = server.split(':')
|
||||
b = int(b)
|
||||
assert c in ['t','h','n']
|
||||
assert c in ['t', 'h', 'n']
|
||||
# set the server
|
||||
if server != self.server:
|
||||
self.server = server
|
||||
|
@ -351,22 +359,24 @@ class Wallet:
|
|||
self.interface.poke()
|
||||
|
||||
def set_path(self, wallet_path):
|
||||
|
||||
"""Set the path of the wallet."""
|
||||
if wallet_path is not None:
|
||||
self.path = wallet_path
|
||||
return
|
||||
# Look for wallet file in the default data directory.
|
||||
# Keeps backwards compatibility.
|
||||
if "HOME" in os.environ:
|
||||
wallet_dir = os.path.join(os.environ["HOME"], ".electrum")
|
||||
elif "LOCALAPPDATA" in os.environ:
|
||||
wallet_dir = os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
|
||||
elif "APPDATA" in os.environ:
|
||||
wallet_dir = os.path.join(os.environ["APPDATA"], "Electrum")
|
||||
else:
|
||||
# backward compatibility: look for wallet file in the default data directory
|
||||
if "HOME" in os.environ:
|
||||
wallet_dir = os.path.join( os.environ["HOME"], '.electrum')
|
||||
elif "LOCALAPPDATA" in os.environ:
|
||||
wallet_dir = os.path.join( os.environ["LOCALAPPDATA"], 'Electrum' )
|
||||
elif "APPDATA" in os.environ:
|
||||
wallet_dir = os.path.join( os.environ["APPDATA"], 'Electrum' )
|
||||
else:
|
||||
raise BaseException("No home directory found in environment variables.")
|
||||
|
||||
if not os.path.exists( wallet_dir ): os.mkdir( wallet_dir )
|
||||
self.path = os.path.join( wallet_dir, 'electrum.dat' )
|
||||
raise BaseException("No home directory found in environment variables.")
|
||||
# Make wallet directory if it does not yet exist.
|
||||
if not os.path.exists(wallet_dir):
|
||||
os.mkdir(wallet_dir)
|
||||
self.path = os.path.join(wallet_dir, "electrum.dat")
|
||||
|
||||
def import_key(self, keypair, password):
|
||||
address, key = keypair.split(':')
|
||||
|
@ -676,19 +686,19 @@ class Wallet:
|
|||
os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
|
||||
|
||||
def read(self):
|
||||
"""Read the contents of the wallet file."""
|
||||
import interface
|
||||
|
||||
upgrade_msg = """This wallet seed is deprecated. Please run upgrade.py for a diagnostic."""
|
||||
self.file_exists = False
|
||||
try:
|
||||
f = open(self.path,"r")
|
||||
data = f.read()
|
||||
f.close()
|
||||
except:
|
||||
with open(self.path, "r") as f:
|
||||
data = f.read()
|
||||
except IOError:
|
||||
return
|
||||
try:
|
||||
d = ast.literal_eval( data )
|
||||
d = ast.literal_eval( data ) #parse raw data from reading wallet file
|
||||
interface.old_to_new(d)
|
||||
|
||||
self.seed_version = d.get('seed_version')
|
||||
self.master_public_key = d.get('master_public_key').decode('hex')
|
||||
self.use_encryption = d.get('use_encryption')
|
||||
|
@ -714,12 +724,12 @@ class Wallet:
|
|||
self.conversion_currency = d.get('conversion_currency', 'USD')
|
||||
self.theme = d.get('theme', 'Cleanlook')
|
||||
except:
|
||||
raise BaseException("cannot read wallet file")
|
||||
raise IOError("Cannot read wallet file.")
|
||||
|
||||
self.update_tx_history()
|
||||
|
||||
if self.seed_version != SEED_VERSION:
|
||||
raise BaseException(upgrade_msg)
|
||||
raise ValueError("This wallet seed is deprecated. Please run upgrade.py for a diagnostic.")
|
||||
|
||||
if self.remote_url: assert self.master_public_key.encode('hex') == self.get_remote_mpk()
|
||||
|
||||
|
@ -838,7 +848,7 @@ class Wallet:
|
|||
try:
|
||||
d.decode('hex')
|
||||
except:
|
||||
raise BaseException("Invalid password")
|
||||
raise ValueError("Invalid password")
|
||||
return d
|
||||
else:
|
||||
return s
|
||||
|
@ -921,10 +931,10 @@ class Wallet:
|
|||
|
||||
def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, from_addr= None):
|
||||
if not self.is_valid(to_address):
|
||||
raise BaseException("Invalid address")
|
||||
raise ValueError("Invalid address")
|
||||
inputs, total, fee = self.choose_tx_inputs( amount, fee, from_addr )
|
||||
if not inputs:
|
||||
raise BaseException("Not enough funds")
|
||||
raise ValueError("Not enough funds")
|
||||
|
||||
if not self.use_change and not change_addr:
|
||||
change_addr = inputs[0][0]
|
||||
|
@ -995,7 +1005,7 @@ class Wallet:
|
|||
self.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
|
||||
|
||||
if not self.is_valid(target):
|
||||
raise BaseException("Invalid bitcoin address")
|
||||
raise ValueError("Invalid bitcoin address")
|
||||
|
||||
return target, signing_addr, auth_name
|
||||
|
||||
|
|
22
setup.py
22
setup.py
|
@ -7,15 +7,12 @@ from lib.version import ELECTRUM_VERSION as version
|
|||
import lib.util as util
|
||||
import os, sys, platform
|
||||
from lib.util import print_error
|
||||
|
||||
if sys.version_info[:3] < (2,6,0):
|
||||
print_error("Error: Electrum requires Python version >= 2.6.0...")
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit("Error: Electrum requires Python version >= 2.6.0...")
|
||||
|
||||
data_files = []
|
||||
|
||||
if platform.system() != 'Windows' and platform.system() != 'Darwin':
|
||||
if (len(sys.argv) > 1 and (sys.argv[1] == "sdist")) or (platform.system() != 'Windows' and platform.system() != 'Darwin'):
|
||||
print "Including all files"
|
||||
data_files += [
|
||||
('/usr/share/applications/',['electrum.desktop']),
|
||||
('/usr/share/app-install/icons/',['electrum.png'])
|
||||
|
@ -27,10 +24,15 @@ if platform.system() != 'Windows' and platform.system() != 'Darwin':
|
|||
data_files.append( ('/usr/share/locale/%s/LC_MESSAGES'%lang, ['locale/%s/LC_MESSAGES/electrum.mo'%lang]) )
|
||||
|
||||
data_files += [
|
||||
(util.appdata_dir(), ["data/background.png", "data/style.css"]),
|
||||
(os.path.join(util.appdata_dir(), "icons"), [
|
||||
"data/icons/confirmed.png",
|
||||
"data/icons/unconfirmed.png"
|
||||
(util.appdata_dir(), ["data/noface.svg", "data/README"]),
|
||||
(os.path.join(util.appdata_dir(), "cleanlook"), [
|
||||
"data/cleanlook/name.cfg",
|
||||
"data/cleanlook/style.css"
|
||||
]),
|
||||
(os.path.join(util.appdata_dir(), "dark"), [
|
||||
"data/dark/background.png",
|
||||
"data/dark/name.cfg",
|
||||
"data/dark/style.css"
|
||||
])
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue