Fix merge conflict with master

This commit is contained in:
Maran 2012-08-29 22:54:44 +02:00
commit 23d314462f
11 changed files with 229 additions and 112 deletions

1
TODO
View File

@ -3,6 +3,7 @@ Client:
- Wizard - Wizard
- Multiple wallets - Multiple wallets
- Themes - Themes
- Extend GUI with history view (View -> Show History)
- Settings window - Settings window
Server: Server:

View File

@ -52,7 +52,7 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1"> id="layer1">
<path <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" 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" id="path3050"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -16,7 +16,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re, sys import re
import sys
# import argparse
import optparse
try: try:
from lib.util import print_error from lib.util import print_error
except ImportError: except ImportError:
@ -24,22 +28,19 @@ except ImportError:
try: try:
import ecdsa import ecdsa
except: except ImportError:
print_error("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'") sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
sys.exit(1)
try: try:
import aes import aes
except: except ImportError:
print_error("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'") sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
sys.exit(1)
try: try:
from lib import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password from lib import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password
except ImportError: except ImportError:
from electrum import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password from electrum import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password
from optparse import OptionParser
from decimal import Decimal from decimal import Decimal
known_commands = { known_commands = {
@ -77,9 +78,9 @@ options:\n --fee, -f: set transaction fee\n --fromaddr, -s: send from address
'import': 'import':
'Imports a key pair\nSyntax: import <address>:<privatekey>', 'Imports a key pair\nSyntax: import <address>:<privatekey>',
'signmessage': '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': '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': 'eval':
"Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"", "Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"",
'deseed': 'deseed':
@ -101,7 +102,7 @@ protected_commands = ['payto', 'password', 'mktx', 'seed', 'import','signmessage
if __name__ == '__main__': if __name__ == '__main__':
usage = "usage: %prog [options] command\nCommands: "+ (', '.join(known_commands)) 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("-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("-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") parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
@ -128,7 +129,9 @@ if __name__ == '__main__':
else: else:
cmd = args[0] cmd = args[0]
firstarg = args[1] if len(args) > 1 else '' 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 cmd == 'gui':
if options.gui=='gtk': if options.gui=='gtk':
@ -165,16 +168,17 @@ if __name__ == '__main__':
except ImportError: except ImportError:
import electrum.gui_qt as gui import electrum.gui_qt as gui
else: else:
#use the lite version if no toolkit specified
try: try:
import lib.gui_lite as gui import lib.gui_lite as gui
except ImportError: except ImportError:
import electrum.gui_lite as gui import electrum.gui_lite as gui
else: else:
print_error("Error: Unknown GUI: " + options.gui) sys.exit("Error: Unknown GUI: " + options.gui)
exit(1)
gui = gui.ElectrumGui(wallet) gui = gui.ElectrumGui(wallet)
WalletSynchronizer(wallet,True).start() interface = WalletSynchronizer(wallet, True, gui.server_list_changed)
interface.start()
try: try:
found = wallet.file_exists found = wallet.file_exists
@ -198,15 +202,13 @@ if __name__ == '__main__':
cmd = 'help' cmd = 'help'
if not wallet.file_exists and cmd not in ['help','create','restore']: if not wallet.file_exists and cmd not in ['help','create','restore']:
print_error("Error: Wallet file not found.") print "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 "Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option"
sys.exit(0) sys.exit(0)
if cmd in ['create', 'restore']: if cmd in ['create', 'restore']:
if wallet.file_exists: if wallet.file_exists:
print_error("Error: Remove the existing wallet first!") sys.exit("Error: Remove the existing wallet first!")
sys.stderr.flush()
sys.exit(0)
password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):") password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
w_host, w_port, w_protocol = wallet.server.split(':') w_host, w_port, w_protocol = wallet.server.split(':')
@ -230,8 +232,7 @@ if __name__ == '__main__':
print_error("Warning: Not hex, trying decode.") print_error("Warning: Not hex, trying decode.")
seed = mnemonic.mn_decode( seed.split(' ') ) seed = mnemonic.mn_decode( seed.split(' ') )
if not seed: if not seed:
print_error("Error: No seed") sys.exit("Error: No seed")
sys.exit(1)
wallet.seed = str(seed) wallet.seed = str(seed)
wallet.init_mpk( wallet.seed ) wallet.init_mpk( wallet.seed )
@ -316,7 +317,6 @@ if __name__ == '__main__':
cmd2 = firstarg cmd2 = firstarg
if cmd2 not in known_commands: if cmd2 not in known_commands:
parser.print_help() parser.print_help()
print
print "Type 'electrum help <command>' to see the help for a specific command" print "Type 'electrum help <command>' to see the help for a specific command"
print "Type 'electrum --help' to see the list of options" print "Type 'electrum --help' to see the list of options"
print "List of commands:", ', '.join(known_commands) print "List of commands:", ', '.join(known_commands)
@ -355,17 +355,15 @@ if __name__ == '__main__':
f = open(ns,'r') f = open(ns,'r')
data = f.read() data = f.read()
f.close() f.close()
except: except IOError:
print_error("Error: Seed file not found") sys.exit("Error: Seed file not found")
sys.exit()
try: try:
import ast import ast
d = ast.literal_eval( data ) d = ast.literal_eval( data )
seed = d['seed'] seed = d['seed']
imported_keys = d.get('imported_keys',{}) imported_keys = d.get('imported_keys',{})
except: except:
print_error("Error: Error with seed file") sys.exit("Error: Error with seed file")
sys.exit(1)
mpk = wallet.master_public_key mpk = wallet.master_public_key
wallet.seed = seed wallet.seed = seed
@ -511,9 +509,8 @@ if __name__ == '__main__':
elif cmd == 'password': elif cmd == 'password':
try: try:
seed = wallet.pw_decode( wallet.seed, password) seed = wallet.pw_decode( wallet.seed, password)
except: except ValueError:
print_error("Error: Password does not decrypt this wallet.") sys.exit("Error: Password does not decrypt this wallet.")
sys.exit(1)
new_password = prompt_password('New password:') new_password = prompt_password('New password:')
wallet.update_password(seed, password, new_password) wallet.update_password(seed, password, new_password)
@ -543,7 +540,8 @@ if __name__ == '__main__':
try: try:
wallet.verify_message(address, signature, message) wallet.verify_message(address, signature, message)
print True print True
except: except BaseException as e:
print "Verification error: {0}".format(e)
print False print False
elif cmd == 'freeze': elif cmd == 'freeze':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -31,7 +31,7 @@ class Exchanger(threading.Thread):
connection = httplib.HTTPSConnection('intersango.com') connection = httplib.HTTPSConnection('intersango.com')
connection.request("GET", "/api/ticker.php") connection.request("GET", "/api/ticker.php")
response = connection.getresponse() response = connection.getresponse()
if response.status == 404: if response.reason == httplib.responses[httplib.NOT_FOUND]:
return return
response = json.loads(response.read()) response = json.loads(response.read())
# 1 = BTC:GBP # 1 = BTC:GBP
@ -40,16 +40,16 @@ class Exchanger(threading.Thread):
# 4 = BTC:PLN # 4 = BTC:PLN
quote_currencies = {} quote_currencies = {}
try: try:
quote_currencies["GBP"] = self.lookup_rate(response, 1) quote_currencies["GBP"] = self._lookup_rate(response, 1)
quote_currencies["EUR"] = self.lookup_rate(response, 2) quote_currencies["EUR"] = self._lookup_rate(response, 2)
quote_currencies["USD"] = self.lookup_rate(response, 3) quote_currencies["USD"] = self._lookup_rate(response, 3)
with self.lock: with self.lock:
self.quote_currencies = quote_currencies self.quote_currencies = quote_currencies
self.parent.emit(SIGNAL("refresh_balance()")) self.parent.emit(SIGNAL("refresh_balance()"))
except KeyError: except KeyError:
pass pass
def lookup_rate(self, response, quote_id): def _lookup_rate(self, response, quote_id):
return decimal.Decimal(response[str(quote_id)]["last"]) return decimal.Decimal(response[str(quote_id)]["last"])
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -4,6 +4,7 @@ from PyQt4.QtCore import *
from PyQt4.QtGui import * from PyQt4.QtGui import *
from decimal import Decimal as D from decimal import Decimal as D
from interface import DEFAULT_SERVERS
from util import get_resource_path as rsrc from util import get_resource_path as rsrc
from i18n import _ from i18n import _
import decimal import decimal
@ -41,14 +42,18 @@ def resize_line_edit_width(line_edit, text_input):
text_input += "A" text_input += "A"
line_edit.setMinimumWidth(metrics.width(text_input)) line_edit.setMinimumWidth(metrics.width(text_input))
class ElectrumGui: class ElectrumGui(QObject):
def __init__(self, wallet): def __init__(self, wallet):
super(QObject, self).__init__()
self.wallet = wallet self.wallet = wallet
self.app = QApplication(sys.argv) self.app = QApplication(sys.argv)
def main(self, url): def main(self, url):
actuator = MiniActuator(self.wallet) actuator = MiniActuator(self.wallet)
self.connect(self, SIGNAL("updateservers()"),
actuator.update_servers_list)
# Should probably not modify the current path but instead # Should probably not modify the current path but instead
# change the behaviour of rsrc(...) # change the behaviour of rsrc(...)
old_path = QDir.currentPath() old_path = QDir.currentPath()
@ -73,7 +78,11 @@ class ElectrumGui:
self.app.exec_() self.app.exec_()
def server_list_changed(self):
self.emit(SIGNAL("updateservers()"))
def expand(self): def expand(self):
"""Hide the lite mode window and show pro-mode."""
self.mini.hide() self.mini.hide()
self.expert.show() self.expert.show()
@ -174,13 +183,15 @@ class MiniWindow(QDialog):
menubar = QMenuBar() menubar = QMenuBar()
electrum_menu = menubar.addMenu(_("&Bitcoin")) electrum_menu = menubar.addMenu(_("&Bitcoin"))
self.servers_menu = electrum_menu.addMenu(_("&Servers")) servers_menu = electrum_menu.addMenu(_("&Servers"))
self.servers_menu.addAction(_("Foo")) servers_group = QActionGroup(self)
self.actuator.set_servers_gui_stuff(servers_menu, servers_group)
self.actuator.populate_servers_menu()
electrum_menu.addSeparator() electrum_menu.addSeparator()
brain_seed = electrum_menu.addAction(_("&BrainWallet Info")) brain_seed = electrum_menu.addAction(_("&BrainWallet Info"))
brain_seed.triggered.connect(self.actuator.show_seed_dialog) brain_seed.triggered.connect(self.actuator.show_seed_dialog)
quit_option = electrum_menu.addAction(_("&Quit"))
electrum_menu.addAction(_("&Quit")) quit_option.triggered.connect(self.close)
view_menu = menubar.addMenu(_("&View")) view_menu = menubar.addMenu(_("&View"))
expert_gui = view_menu.addAction(_("&Pro Mode")) expert_gui = view_menu.addAction(_("&Pro Mode"))
@ -254,9 +265,10 @@ class MiniWindow(QDialog):
pass pass
def set_quote_currency(self, currency): def set_quote_currency(self, currency):
"""Set and display the fiat currency country."""
assert currency in self.quote_currencies assert currency in self.quote_currencies
self.quote_currencies.remove(currency) self.quote_currencies.remove(currency)
self.quote_currencies = [currency] + self.quote_currencies self.quote_currencies.insert(0, currency)
self.refresh_balance() self.refresh_balance()
def change_quote_currency(self): def change_quote_currency(self):
@ -274,6 +286,7 @@ class MiniWindow(QDialog):
self.amount_input_changed(self.amount_input.text()) self.amount_input_changed(self.amount_input.text())
def set_balances(self, btc_balance): def set_balances(self, btc_balance):
"""Set the bitcoin balance and update the amount label accordingly."""
self.btc_balance = btc_balance self.btc_balance = btc_balance
quote_text = self.create_quote_text(btc_balance) quote_text = self.create_quote_text(btc_balance)
if quote_text: if quote_text:
@ -283,6 +296,7 @@ class MiniWindow(QDialog):
self.setWindowTitle("Electrum - %s BTC" % btc_balance) self.setWindowTitle("Electrum - %s BTC" % btc_balance)
def amount_input_changed(self, amount_text): def amount_input_changed(self, amount_text):
"""Update the number of bitcoins displayed."""
self.check_button_status() self.check_button_status()
try: try:
@ -298,6 +312,8 @@ class MiniWindow(QDialog):
self.balance_label.show_balance() self.balance_label.show_balance()
def create_quote_text(self, btc_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_currency = self.quote_currencies[0]
quote_balance = self.exchanger.exchange(btc_balance, quote_currency) quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
if quote_balance is None: if quote_balance is None:
@ -314,8 +330,16 @@ class MiniWindow(QDialog):
self.amount_input.setText("") self.amount_input.setText("")
def check_button_status(self): 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 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) self.send_button.setDisabled(False)
else: else:
self.send_button.setDisabled(True) self.send_button.setDisabled(True)
@ -385,10 +409,12 @@ class BalanceLabel(QLabel):
self.amount_text = "" self.amount_text = ""
def mousePressEvent(self, event): def mousePressEvent(self, event):
"""Change the fiat currency selection if window background is clicked."""
if self.state != self.SHOW_CONNECTING: if self.state != self.SHOW_CONNECTING:
self.change_quote_currency() self.change_quote_currency()
def set_balance_text(self, btc_balance, quote_text): def set_balance_text(self, btc_balance, quote_text):
"""Set the amount of bitcoins in the gui."""
if self.state == self.SHOW_CONNECTING: if self.state == self.SHOW_CONNECTING:
self.state = self.SHOW_BALANCE 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) 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.setMouseTracking(True)
self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment")) 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.layout().setSizeConstraint(QLayout.SetFixedSize)
#self.setFrameStyle(QFrame.WinPanel|QFrame.Raised) #self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
#self.setAlignment(Qt.AlignCenter) #self.setAlignment(Qt.AlignCenter)
@ -480,33 +507,43 @@ class ReceivePopup(QDialog):
self.show() self.show()
class MiniActuator: class MiniActuator:
"""Initialize the definitions relating to themes and
sending/recieving bitcoins."""
def __init__(self, wallet): def __init__(self, wallet):
"""Retrieve the gui theme used in previous session."""
self.wallet = wallet self.wallet = wallet
self.theme_name = self.wallet.theme self.theme_name = self.wallet.theme
self.themes = util.load_theme_paths() self.themes = util.load_theme_paths()
def load_theme(self): def load_theme(self):
"""Load theme retrieved from wallet file."""
try: try:
theme_prefix, theme_path = self.themes[self.theme_name] theme_prefix, theme_path = self.themes[self.theme_name]
except KeyError: except KeyError:
util.print_error("Theme not found! ", self.theme_name) util.print_error("Theme not found!", self.theme_name)
return return
QDir.setCurrent(os.path.join(theme_prefix, theme_path)) QDir.setCurrent(os.path.join(theme_prefix, theme_path))
with open(rsrc("style.css")) as style_file: with open(rsrc("style.css")) as style_file:
qApp.setStyleSheet(style_file.read()) qApp.setStyleSheet(style_file.read())
def theme_names(self): def theme_names(self):
return self.themes.keys() """Sort themes."""
return sorted(self.themes.keys())
def selected_theme(self): def selected_theme(self):
"""Select theme."""
return self.theme_name return self.theme_name
def change_theme(self, theme_name): def change_theme(self, theme_name):
"""Change theme."""
self.wallet.theme = self.theme_name = theme_name self.wallet.theme = self.theme_name = theme_name
self.load_theme() self.load_theme()
def set_configured_currency(self, set_quote_currency): 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 = self.wallet.conversion_currency
# currency can be none when Electrum is used for the first # currency can be none when Electrum is used for the first
# time and no setting has been created yet. # time and no setting has been created yet.
@ -514,9 +551,70 @@ class MiniActuator:
set_quote_currency(currency) set_quote_currency(currency)
def set_config_currency(self, conversion_currency): def set_config_currency(self, conversion_currency):
"""Change the wallet fiat currency country."""
self.wallet.conversion_currency = conversion_currency 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): def copy_address(self, receive_popup):
"""Copy the wallet addresses into the client."""
addrs = [addr for addr in self.wallet.all_addresses() addrs = [addr for addr in self.wallet.all_addresses()
if not self.wallet.is_change(addr)] if not self.wallet.is_change(addr)]
# Select most recent addresses from gap limit # Select most recent addresses from gap limit
@ -527,6 +625,7 @@ class MiniActuator:
receive_popup.popup() receive_popup.popup()
def send(self, address, amount, parent_window): def send(self, address, amount, parent_window):
"""Send bitcoins to the target address."""
dest_address = self.fetch_destination(address) dest_address = self.fetch_destination(address)
if dest_address is None or not self.wallet.is_valid(dest_address): if dest_address is None or not self.wallet.is_valid(dest_address):
@ -589,6 +688,7 @@ class MiniActuator:
return recipient return recipient
def is_valid(self, address): def is_valid(self, address):
"""Check if bitcoin address is valid."""
return self.wallet.is_valid(address) return self.wallet.is_valid(address)
def acceptbit(self, currency): def acceptbit(self, currency):

View File

@ -23,9 +23,7 @@ from util import print_error
try: try:
import PyQt4 import PyQt4
except: except:
print_error("Error: Could not import PyQt4") sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
print_error("on Linux systems, you may try 'sudo apt-get install python-qt4'")
sys.exit(1)
from PyQt4.QtGui import * from PyQt4.QtGui import *
from PyQt4.QtCore import * from PyQt4.QtCore import *
@ -36,9 +34,7 @@ from interface import DEFAULT_SERVERS
try: try:
import icons_rc import icons_rc
except: except:
print_error("Error: Could not import icons_rc.py") sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
print_error("Please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
sys.exit(1)
from wallet import format_satoshis from wallet import format_satoshis
import bmp, mnemonic, pyqrnative, qrscanner import bmp, mnemonic, pyqrnative, qrscanner
@ -1434,6 +1430,9 @@ class ElectrumGui:
if app is None: if app is None:
self.app = QApplication(sys.argv) self.app = QApplication(sys.argv)
def server_list_changed(self):
pass
def waiting_dialog(self): def waiting_dialog(self):
s = Timer() s = Timer()

View File

@ -27,7 +27,7 @@ DEFAULT_TIMEOUT = 5
DEFAULT_SERVERS = [ 'ecdsa.org:50001:t', DEFAULT_SERVERS = [ 'ecdsa.org:50001:t',
'electrum.novit.ro:50001:t', 'electrum.novit.ro:50001:t',
'uncle-enzo.info: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): def replace_keys(obj, old_key, new_key):
@ -306,12 +306,13 @@ class TcpStratumInterface(Interface):
class WalletSynchronizer(threading.Thread): class WalletSynchronizer(threading.Thread):
def __init__(self, wallet, loop=False): def __init__(self, wallet, loop=False, servers_loaded_callback=None):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.daemon = True self.daemon = True
self.wallet = wallet self.wallet = wallet
self.loop = loop self.loop = loop
self.init_interface() self.init_interface()
self.servers_loaded_callback = servers_loaded_callback
def init_interface(self): def init_interface(self):
try: try:
@ -334,7 +335,6 @@ class WalletSynchronizer(threading.Thread):
self.interface = InterfaceClass(host, port, self.wallet.debug_server) self.interface = InterfaceClass(host, port, self.wallet.debug_server)
self.wallet.interface = self.interface self.wallet.interface = self.interface
def handle_response(self, r): def handle_response(self, r):
if r is None: if r is None:
return return
@ -354,15 +354,19 @@ class WalletSynchronizer(threading.Thread):
host = item[1] host = item[1]
ports = [] ports = []
version = None version = None
if len(item)>2: if len(item) > 2:
for v in item[2]: for v in item[2]:
if re.match("[th]\d+",v): if re.match("[th]\d+", v):
ports.append((v[0],v[1:])) ports.append((v[0], v[1:]))
if re.match("v(.?)+",v): if re.match("v(.?)+", v):
version = v[1:] version = v[1:]
if ports and version: if ports and version:
servers.append( (host, ports) ) servers.append((host, ports))
self.interface.servers = servers 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': elif method == 'blockchain.address.subscribe':
addr = params[0] addr = params[0]
@ -425,6 +429,7 @@ class WalletSynchronizer(threading.Thread):
self.wallet.trigger_callbacks() self.wallet.trigger_callbacks()
if self.loop: if self.loop:
time.sleep(5) time.sleep(5)
# Server has been changed. Copy callback for new interface.
self.init_interface() self.init_interface()
self.start_interface() self.start_interface()
continue continue

View File

@ -3,12 +3,13 @@ import platform
import sys import sys
def print_error(*args): def print_error(*args):
for item in args: # Stringify args
sys.stderr.write(str(item)) args = [str(item) for item in args]
sys.stderr.write("\n") sys.stderr.write(" ".join(args) + "\n")
sys.stderr.flush() sys.stderr.flush()
def appdata_dir(): def appdata_dir():
"""Find the path to the application data directory; add an electrum folder and return path."""
if platform.system() == "Windows": if platform.system() == "Windows":
return os.path.join(os.environ["APPDATA"], "Electrum") return os.path.join(os.environ["APPDATA"], "Electrum")
elif platform.system() == "Linux": elif platform.system() == "Linux":
@ -23,6 +24,7 @@ def get_resource_path(*args):
return os.path.join(".", *args) return os.path.join(".", *args)
def local_data_dir(): def local_data_dir():
"""Return path to the data folder."""
assert sys.argv assert sys.argv
prefix_path = os.path.dirname(sys.argv[0]) prefix_path = os.path.dirname(sys.argv[0])
local_data = os.path.join(prefix_path, "data") local_data = os.path.join(prefix_path, "data")

View File

@ -16,9 +16,20 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # 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 ecdsa.util import string_to_number, number_to_string
from util import print_error from util import print_error
@ -65,8 +76,7 @@ __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars) __b58base = len(__b58chars)
def b58encode(v): 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 long_value = 0L
for (i, c) in enumerate(v[::-1]): for (i, c) in enumerate(v[::-1]):
@ -89,8 +99,7 @@ def b58encode(v):
return (__b58chars[0]*nPad) + result return (__b58chars[0]*nPad) + result
def b58decode(v, length): def b58decode(v, length):
""" decode v into a string of len bytes """ decode v into a string of len bytes."""
"""
long_value = 0L long_value = 0L
for (i, c) in enumerate(v[::-1]): for (i, c) in enumerate(v[::-1]):
long_value += __b58chars.find(c) * (__b58base**i) long_value += __b58chars.find(c) * (__b58base**i)
@ -157,8 +166,7 @@ def prompt_password(prompt, confirm=True):
password2 = getpass.getpass("Confirm: ") password2 = getpass.getpass("Confirm: ")
if password != password2: if password != password2:
print_error("Error: Passwords do not match.") sys.exit("Error: Passwords do not match.")
sys.exit(1)
else: else:
password = raw_input(prompt) password = raw_input(prompt)
@ -282,7 +290,7 @@ class Wallet:
self.num_zeros = 0 self.num_zeros = 0
self.master_public_key = '' self.master_public_key = ''
self.conversion_currency = None self.conversion_currency = None
self.theme = None self.theme = "Cleanlook"
# saved fields # saved fields
self.use_encryption = False self.use_encryption = False
@ -342,7 +350,7 @@ class Wallet:
# raise an error if the format isnt correct # raise an error if the format isnt correct
a,b,c = server.split(':') a,b,c = server.split(':')
b = int(b) b = int(b)
assert c in ['t','h','n'] assert c in ['t', 'h', 'n']
# set the server # set the server
if server != self.server: if server != self.server:
self.server = server self.server = server
@ -351,22 +359,24 @@ class Wallet:
self.interface.poke() self.interface.poke()
def set_path(self, wallet_path): def set_path(self, wallet_path):
"""Set the path of the wallet."""
if wallet_path is not None: if wallet_path is not None:
self.path = wallet_path 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: else:
# backward compatibility: look for wallet file in the default data directory raise BaseException("No home directory found in environment variables.")
if "HOME" in os.environ: # Make wallet directory if it does not yet exist.
wallet_dir = os.path.join( os.environ["HOME"], '.electrum') if not os.path.exists(wallet_dir):
elif "LOCALAPPDATA" in os.environ: os.mkdir(wallet_dir)
wallet_dir = os.path.join( os.environ["LOCALAPPDATA"], 'Electrum' ) self.path = os.path.join(wallet_dir, "electrum.dat")
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' )
def import_key(self, keypair, password): def import_key(self, keypair, password):
address, key = keypair.split(':') address, key = keypair.split(':')
@ -676,19 +686,19 @@ class Wallet:
os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE) os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
def read(self): def read(self):
"""Read the contents of the wallet file."""
import interface import interface
upgrade_msg = """This wallet seed is deprecated. Please run upgrade.py for a diagnostic."""
self.file_exists = False self.file_exists = False
try: try:
f = open(self.path,"r") with open(self.path, "r") as f:
data = f.read() data = f.read()
f.close() except IOError:
except:
return return
try: try:
d = ast.literal_eval( data ) d = ast.literal_eval( data ) #parse raw data from reading wallet file
interface.old_to_new(d) interface.old_to_new(d)
self.seed_version = d.get('seed_version') self.seed_version = d.get('seed_version')
self.master_public_key = d.get('master_public_key').decode('hex') self.master_public_key = d.get('master_public_key').decode('hex')
self.use_encryption = d.get('use_encryption') self.use_encryption = d.get('use_encryption')
@ -714,12 +724,12 @@ class Wallet:
self.conversion_currency = d.get('conversion_currency', 'USD') self.conversion_currency = d.get('conversion_currency', 'USD')
self.theme = d.get('theme', 'Cleanlook') self.theme = d.get('theme', 'Cleanlook')
except: except:
raise BaseException("cannot read wallet file") raise IOError("Cannot read wallet file.")
self.update_tx_history() self.update_tx_history()
if self.seed_version != SEED_VERSION: 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() if self.remote_url: assert self.master_public_key.encode('hex') == self.get_remote_mpk()
@ -838,7 +848,7 @@ class Wallet:
try: try:
d.decode('hex') d.decode('hex')
except: except:
raise BaseException("Invalid password") raise ValueError("Invalid password")
return d return d
else: else:
return s return s
@ -921,10 +931,10 @@ class Wallet:
def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, from_addr= None): def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, from_addr= None):
if not self.is_valid(to_address): 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 ) inputs, total, fee = self.choose_tx_inputs( amount, fee, from_addr )
if not inputs: if not inputs:
raise BaseException("Not enough funds") raise ValueError("Not enough funds")
if not self.use_change and not change_addr: if not self.use_change and not change_addr:
change_addr = inputs[0][0] change_addr = inputs[0][0]
@ -995,7 +1005,7 @@ class Wallet:
self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) self.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
if not self.is_valid(target): if not self.is_valid(target):
raise BaseException("Invalid bitcoin address") raise ValueError("Invalid bitcoin address")
return target, signing_addr, auth_name return target, signing_addr, auth_name

View File

@ -7,15 +7,12 @@ from lib.version import ELECTRUM_VERSION as version
import lib.util as util import lib.util as util
import os, sys, platform import os, sys, platform
from lib.util import print_error from lib.util import print_error
if sys.version_info[:3] < (2,6,0): if sys.version_info[:3] < (2,6,0):
print_error("Error: Electrum requires Python version >= 2.6.0...") sys.exit("Error: Electrum requires Python version >= 2.6.0...")
sys.exit(1)
data_files = [] data_files = []
if (len(sys.argv) > 1 and (sys.argv[1] == "sdist")) or (platform.system() != 'Windows' and platform.system() != 'Darwin'):
if platform.system() != 'Windows' and platform.system() != 'Darwin': print "Including all files"
data_files += [ data_files += [
('/usr/share/applications/',['electrum.desktop']), ('/usr/share/applications/',['electrum.desktop']),
('/usr/share/app-install/icons/',['electrum.png']) ('/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.append( ('/usr/share/locale/%s/LC_MESSAGES'%lang, ['locale/%s/LC_MESSAGES/electrum.mo'%lang]) )
data_files += [ data_files += [
(util.appdata_dir(), ["data/background.png", "data/style.css"]), (util.appdata_dir(), ["data/noface.svg", "data/README"]),
(os.path.join(util.appdata_dir(), "icons"), [ (os.path.join(util.appdata_dir(), "cleanlook"), [
"data/icons/confirmed.png", "data/cleanlook/name.cfg",
"data/icons/unconfirmed.png" "data/cleanlook/style.css"
]),
(os.path.join(util.appdata_dir(), "dark"), [
"data/dark/background.png",
"data/dark/name.cfg",
"data/dark/style.css"
]) ])
] ]