commit 5444f55e6b497ad8246ada14cff4b941ddcd7996
parent 0d11aa75c48fa8e1daec80afe45ba25f99353ae7
Author: thomasv <thomasv@gitorious>
Date: Thu, 11 Oct 2012 20:10:12 +0200
big refactoring: command line options and electrum.conf options override settings in wallet file.
Diffstat:
13 files changed, 389 insertions(+), 369 deletions(-)
diff --git a/electrum b/electrum
@@ -18,7 +18,6 @@
import re
import sys
-# import argparse
import optparse
try:
@@ -37,9 +36,9 @@ 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, parse_proxy_options, SimpleConfig
+ from lib import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password, SimpleConfig, pick_random_server
except ImportError:
- from electrum import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password, parse_proxy_options, SimpleConfig
+ from electrum import Wallet, WalletSynchronizer, format_satoshis, mnemonic, prompt_password, SimpleConfig, pick_random_server
from decimal import Decimal
@@ -95,33 +94,39 @@ options:\n --fee, -f: set transaction fee\n --fromaddr, -s: send from address
-offline_commands = [ 'password', 'mktx', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval', 'create', 'addresses', 'import', 'seed','deseed','reseed','freeze','unfreeze','prioritize','unprioritize']
+offline_commands = [ 'password', 'mktx',
+ 'label', 'contacts',
+ 'help', 'validateaddress',
+ 'signmessage', 'verifymessage',
+ 'eval', 'create', 'addresses',
+ 'import', 'seed',
+ 'deseed','reseed',
+ 'freeze','unfreeze',
+ 'prioritize','unprioritize']
+
protected_commands = ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ]
if __name__ == '__main__':
- # Load simple config class
- simple_config = SimpleConfig()
-
usage = "usage: %prog [options] command\nCommands: "+ (', '.join(known_commands))
parser = optparse.OptionParser(prog=usage)
- parser.add_option("-g", "--gui", dest="gui", default=simple_config.config["gui"], help="gui")
+ parser.add_option("-g", "--gui", dest="gui", 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")
parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance at listed addresses")
parser.add_option("-k", "--keys",action="store_true", dest="show_keys",default=False, help="show the private keys of listed addresses")
parser.add_option("-f", "--fee", dest="tx_fee", default="0.005", help="set tx fee")
- parser.add_option("-s", "--fromaddr", dest="from_addr", default=None, help="set source address for payto/mktx. if it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet.")
+ parser.add_option("-F", "--fromaddr", dest="from_addr", default=None, help="set source address for payto/mktx. if it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet.")
parser.add_option("-c", "--changeaddr", dest="change_addr", default=None, help="set the change address for payto/mktx. default is a spare address, or the source address if it's not in the wallet")
+ parser.add_option("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is t or h")
parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http")
options, args = parser.parse_args()
- proxy = parse_proxy_options(options.proxy) if options.proxy else simple_config.config["proxy"]
- wallet = Wallet()
- wallet.set_path(options.wallet_path)
- wallet.read()
+ # config is an object passed to the various constructors (wallet, interface, gui)
+ config = SimpleConfig(options)
+ wallet = Wallet(config)
if len(args)==0:
url = None
@@ -136,31 +141,31 @@ if __name__ == '__main__':
#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':
+ pref_gui = config.get('gui','qt')
+ if pref_gui == 'gtk':
try:
import lib.gui as gui
except ImportError:
import electrum.gui as gui
- elif options.gui=='qt':
+ elif pref_gui == 'qt':
try:
import lib.gui_qt as gui
except ImportError:
import electrum.gui_qt as gui
- elif options.gui == 'lite':
+ elif pref_gui == 'lite':
try:
import lib.gui_lite as gui
except ImportError:
import electrum.gui_lite as gui
else:
- sys.exit("Error: Unknown GUI: " + options.gui)
+ sys.exit("Error: Unknown GUI: " + pref_gui )
- gui = gui.ElectrumGui(wallet)
- interface = WalletSynchronizer(wallet, True, gui.server_list_changed, proxy)
+ gui = gui.ElectrumGui(wallet, config)
+ interface = WalletSynchronizer(wallet, config, True, gui.server_list_changed)
interface.start()
try:
- found = wallet.file_exists
+ found = config.wallet_file_exists
if not found:
found = gui.restore_or_create()
except SystemExit, e:
@@ -180,17 +185,19 @@ if __name__ == '__main__':
if cmd not in known_commands:
cmd = 'help'
- if not wallet.file_exists and cmd not in ['help','create','restore']:
+ if not config.wallet_file_exists and cmd not in ['help','create','restore']:
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:
+ if config.wallet_file_exists:
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(':')
+ server = config.get('server')
+ if not server: server = pick_random_server()
+ w_host, w_port, w_protocol = server.split(':')
host = raw_input("server (default:%s):"%w_host)
port = raw_input("port (default:%s):"%w_port)
protocol = raw_input("protocol [t=tcp;h=http;n=native] (default:%s):"%w_protocol)
@@ -199,7 +206,7 @@ if __name__ == '__main__':
if host: w_host = host
if port: w_port = port
if protocol: w_protocol = protocol
- wallet.server = w_host + ':' + w_port + ':' +w_protocol
+ wallet.config.set_key('server', w_host + ':' + w_port + ':' +w_protocol)
if fee: wallet.fee = float(fee)
if gap: wallet.gap_limit = int(gap)
@@ -216,7 +223,7 @@ if __name__ == '__main__':
wallet.seed = str(seed)
wallet.init_mpk( wallet.seed )
if not options.offline:
- WalletSynchronizer(wallet, proxy=proxy).start()
+ WalletSynchronizer(wallet, config).start()
print "Recovering wallet..."
wallet.up_to_date_event.clear()
wallet.up_to_date = False
@@ -239,7 +246,7 @@ if __name__ == '__main__':
print "Please keep it in a safe place; if you lose it, you will not be able to restore your wallet."
print "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:"
print "\""+' '.join(mnemonic.mn_encode(wallet.seed))+"\""
- print "Wallet saved in '%s'"%wallet.path
+ print "Wallet saved in '%s'"%wallet.config.path
if password:
wallet.update_password(wallet.seed, None, password)
@@ -259,7 +266,7 @@ if __name__ == '__main__':
# open session
if cmd not in offline_commands and not options.offline:
- WalletSynchronizer(wallet, proxy=proxy).start()
+ WalletSynchronizer(wallet, config).start()
wallet.update()
wallet.save()
diff --git a/lib/__init__.py b/lib/__init__.py
@@ -1,4 +1,3 @@
from wallet import Wallet, format_satoshis, prompt_password
-from interface import WalletSynchronizer, parse_proxy_options
-from interface import TcpStratumInterface
+from interface import WalletSynchronizer, Interface, pick_random_server
from simple_config import SimpleConfig
diff --git a/lib/gui.py b/lib/gui.py
@@ -558,12 +558,13 @@ class ElectrumWindow:
def show_message(self, msg):
show_message(msg, self.window)
- def __init__(self, wallet):
+ def __init__(self, wallet, config):
+ self.config = config
self.wallet = wallet
self.funds_error = False # True if not enough funds
self.window = MyWindow(gtk.WINDOW_TOPLEVEL)
- title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.path
+ title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
if not self.wallet.seed: title += ' [seedless]'
self.window.set_title(title)
self.window.connect("destroy", gtk.main_quit)
@@ -1298,11 +1299,12 @@ class ElectrumWindow:
class ElectrumGui():
- def __init__(self, wallet):
+ def __init__(self, wallet, config):
self.wallet = wallet
+ self.config = config
def main(self, url=None):
- ew = ElectrumWindow(self.wallet)
+ ew = ElectrumWindow(self.wallet, self.config)
if url: ew.set_url(url)
gtk.main()
diff --git a/lib/gui_lite.py b/lib/gui_lite.py
@@ -13,17 +13,13 @@ except ImportError:
qtVersion = qVersion()
if not(int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
app = QApplication(sys.argv)
- QMessageBox.warning(None,"Could not start Lite GUI.", "Electrum was unable to load the 'Lite GUI' because it needs Qt version >= 4.7.\nElectrum was set to use the 'Qt' GUI")
- from simple_config import SimpleConfig
- cfg = SimpleConfig()
- cfg.set_key("gui", "qt",True)
+ QMessageBox.warning(None,"Could not start Lite GUI.", "Electrum was unable to load the 'Lite GUI' because it needs Qt version >= 4.7.\nPlease use the 'Qt' GUI")
sys.exit(0)
from decimal import Decimal as D
from interface import DEFAULT_SERVERS
-from simple_config import SimpleConfig
from util import get_resource_path as rsrc
from i18n import _
import decimal
@@ -61,10 +57,11 @@ def resize_line_edit_width(line_edit, text_input):
class ElectrumGui(QObject):
- def __init__(self, wallet):
+ def __init__(self, wallet, config):
super(QObject, self).__init__()
self.wallet = wallet
+ self.config = config
self.app = QApplication(sys.argv)
def main(self, url):
@@ -76,7 +73,7 @@ class ElectrumGui(QObject):
old_path = QDir.currentPath()
actuator.load_theme()
- self.mini = MiniWindow(actuator, self.expand)
+ self.mini = MiniWindow(actuator, self.expand, self.config)
driver = MiniDriver(self.wallet, self.mini)
# Reset path back to original value now that loading the GUI
@@ -88,12 +85,10 @@ class ElectrumGui(QObject):
timer = Timer()
timer.start()
- self.expert = gui_qt.ElectrumWindow(self.wallet)
+ self.expert = gui_qt.ElectrumWindow(self.wallet, self.config)
self.expert.app = self.app
self.expert.connect_slots(timer)
self.expert.update_wallet()
-
-
self.app.exec_()
def server_list_changed(self):
@@ -124,10 +119,11 @@ class ElectrumGui(QObject):
class MiniWindow(QDialog):
- def __init__(self, actuator, expand_callback):
+ def __init__(self, actuator, expand_callback, config):
super(MiniWindow, self).__init__()
self.actuator = actuator
+ self.config = config
self.btc_balance = None
self.quote_currencies = ["EUR", "USD", "GBP"]
@@ -259,11 +255,12 @@ class MiniWindow(QDialog):
close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
close_shortcut.activated.connect(self.close)
- self.cfg = SimpleConfig()
- g = self.cfg.config["winpos-lite"]
+ g = self.config.get("winpos-lite",[4, 25, 351, 149])
self.setGeometry(g[0], g[1], g[2], g[3])
- show_history.setChecked(self.cfg.config["history"])
- self.show_history(self.cfg.config["history"])
+
+ show_hist = self.config.get("gui_show_history",False)
+ show_history.setChecked(show_hist)
+ self.show_history(show_hist)
self.setWindowIcon(QIcon(":electrum.png"))
self.setWindowTitle("Electrum")
@@ -282,9 +279,8 @@ class MiniWindow(QDialog):
def closeEvent(self, event):
g = self.geometry()
- self.cfg.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()])
- self.cfg.set_key("history", self.history_list.isVisible())
- self.cfg.save_config()
+ self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
+ self.config.set_key("history", self.history_list.isVisible(),True)
super(MiniWindow, self).closeEvent(event)
qApp.quit()
@@ -563,7 +559,7 @@ class MiniActuator:
def __init__(self, wallet):
"""Retrieve the gui theme used in previous session."""
self.wallet = wallet
- self.theme_name = self.wallet.theme
+ self.theme_name = self.wallet.config.get('litegui_theme','Cleanlook')
self.themes = util.load_theme_paths()
def load_theme(self):
@@ -587,13 +583,14 @@ class MiniActuator:
def change_theme(self, theme_name):
"""Change theme."""
- self.wallet.theme = self.theme_name = theme_name
+ self.theme_name = theme_name
+ self.wallet.config.set_key('litegui_theme',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 = self.wallet.config.get('conversion_currency')
# currency can be none when Electrum is used for the first
# time and no setting has been created yet.
if currency is not None:
@@ -601,7 +598,7 @@ class MiniActuator:
def set_config_currency(self, conversion_currency):
"""Change the wallet fiat currency country."""
- self.wallet.conversion_currency = conversion_currency
+ self.wallet.config.set_key('conversion_currency',conversion_currency,True)
def set_servers_gui_stuff(self, servers_menu, servers_group):
self.servers_menu = servers_menu
@@ -620,7 +617,7 @@ class MiniActuator:
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]
+ current_server = interface.server.split(":")[0]
for server_name in server_names:
server_action = self.servers_menu.addAction(server_name)
server_action.setCheckable(True)
@@ -661,8 +658,7 @@ class MiniActuator:
server_line = "%s:%s:%s" % (server_name, port, protocol)
# Should this have exception handling?
- self.cfg = SimpleConfig()
- self.wallet.set_server(server_line, self.cfg.config["proxy"])
+ self.wallet.set_server(server_line, self.config.get(["proxy"]))
def copy_address(self, receive_popup):
"""Copy the wallet addresses into the client."""
diff --git a/lib/gui_qt.py b/lib/gui_qt.py
@@ -37,9 +37,7 @@ except:
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
-from simple_config import SimpleConfig
import bmp, mnemonic, pyqrnative, qrscanner
-from simple_config import SimpleConfig
from decimal import Decimal
@@ -185,11 +183,14 @@ def ok_cancel_buttons(dialog):
class ElectrumWindow(QMainWindow):
- def __init__(self, wallet):
+ def __init__(self, wallet, config):
QMainWindow.__init__(self)
self.wallet = wallet
+ self.config = config
self.wallet.register_callback(self.update_callback)
+ self.detailed_view = config.get('qt_detailed_view', False)
+
self.funds_error = False
self.completions = QStringListModel()
@@ -204,10 +205,10 @@ class ElectrumWindow(QMainWindow):
tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setCentralWidget(tabs)
self.create_status_bar()
- cfg = SimpleConfig()
- g = cfg.config["winpos-qt"]
+
+ g = self.config.get("winpos-qt",[100, 100, 840, 400])
self.setGeometry(g[0], g[1], g[2], g[3])
- title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.path
+ title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
if not self.wallet.seed: title += ' [seedless]'
self.setWindowTitle( title )
@@ -696,10 +697,12 @@ class ElectrumWindow(QMainWindow):
return w
def details_button_text(self):
- return _('Hide details') if self.wallet.gui_detailed_view else _('Show details')
+ return _('Hide details') if self.detailed_view else _('Show details')
def toggle_detailed_view(self):
- self.wallet.gui_detailed_view = not self.wallet.gui_detailed_view
+ self.detailed_view = not self.detailed_view
+ self.config.set_key('qt_detailed_view', self.detailed_view, True)
+
self.details_button.setText(self.details_button_text())
self.wallet.save()
self.update_receive_tab()
@@ -731,11 +734,11 @@ class ElectrumWindow(QMainWindow):
menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
menu.addAction(_("Edit label"), lambda: self.edit_label(True))
- if self.wallet.gui_detailed_view:
- t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
- menu.addAction(t, lambda: self.toggle_freeze(addr))
- t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
- menu.addAction(t, lambda: self.toggle_priority(addr))
+
+ t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
+ menu.addAction(t, lambda: self.toggle_freeze(addr))
+ t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
+ menu.addAction(t, lambda: self.toggle_priority(addr))
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
@@ -790,9 +793,9 @@ class ElectrumWindow(QMainWindow):
def update_receive_tab(self):
l = self.receive_list
l.clear()
- l.setColumnHidden(0,not self.wallet.gui_detailed_view)
- l.setColumnHidden(3,not self.wallet.gui_detailed_view)
- l.setColumnHidden(4,not self.wallet.gui_detailed_view)
+ l.setColumnHidden(0,not self.detailed_view)
+ l.setColumnHidden(3,not self.detailed_view)
+ l.setColumnHidden(4,not self.detailed_view)
l.setColumnWidth(0, 50)
l.setColumnWidth(1, 310)
l.setColumnWidth(2, 250)
@@ -803,7 +806,7 @@ class ElectrumWindow(QMainWindow):
is_red = False
for address in self.wallet.all_addresses():
- if self.wallet.is_change(address) and not self.wallet.gui_detailed_view:
+ if self.wallet.is_change(address) and not self.detailed_view:
continue
label = self.wallet.labels.get(address,'')
@@ -855,7 +858,7 @@ class ElectrumWindow(QMainWindow):
l = self.contacts_list
l.clear()
- l.setColumnHidden(2, not self.wallet.gui_detailed_view)
+ l.setColumnHidden(2, not self.detailed_view)
l.setColumnWidth(0, 350)
l.setColumnWidth(1, 330)
l.setColumnWidth(2, 100)
@@ -1247,9 +1250,8 @@ class ElectrumWindow(QMainWindow):
gap_e.textChanged.connect(lambda: numbify(nz_e,True))
gui = QComboBox()
- gui.addItems(['Lite', 'Qt'])
- cfg = SimpleConfig()
- gui.setCurrentIndex(gui.findText(cfg.config["gui"].capitalize()))
+ gui.addItems(['Lite', 'Qt', 'Gtk'])
+ gui.setCurrentIndex(gui.findText(self.config.get("gui","lite").capitalize()))
grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
grid.addWidget(gui, 7, 1)
grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
@@ -1298,9 +1300,7 @@ class ElectrumWindow(QMainWindow):
else:
QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
- cfg = SimpleConfig()
- cfg.set_key("gui", str(gui.currentText()).lower())
- cfg.save_config()
+ self.config.set_key("gui", str(gui.currentText()).lower(), True)
@@ -1312,7 +1312,7 @@ class ElectrumWindow(QMainWindow):
status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
else:
status = _("Not connected")
- server = wallet.server
+ server = interface.server
else:
import random
status = _("Please choose a server.")
@@ -1458,37 +1458,28 @@ class ElectrumWindow(QMainWindow):
if not d.exec_(): return
server = unicode( host_line.text() )
- try:
- if proxy_mode.currentText() != 'NONE':
- proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
- else:
- proxy = None
+ if proxy_mode.currentText() != 'NONE':
+ proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
+ else:
+ proxy = None
- cfg = SimpleConfig()
- cfg.set_key("proxy", proxy, True)
- wallet.set_server(server, proxy)
+ wallet.config.set_key("proxy", proxy, True)
+ wallet.config.set_key("server", server, True)
+ interface.set_server(server, proxy)
- except Exception as err:
- QMessageBox.information(None, _('Error'), str(err), _('OK'))
- if parent == None:
- sys.exit(1)
- else:
- return
-
return True
def closeEvent(self, event):
- cfg = SimpleConfig()
g = self.geometry()
- cfg.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()])
- cfg.save_config()
+ self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
event.accept()
class ElectrumGui:
- def __init__(self, wallet, app=None):
+ def __init__(self, wallet, config, app=None):
self.wallet = wallet
+ self.config = config
if app is None:
self.app = QApplication(sys.argv)
@@ -1563,7 +1554,7 @@ class ElectrumGui:
def main(self,url):
s = Timer()
s.start()
- w = ElectrumWindow(self.wallet)
+ w = ElectrumWindow(self.wallet, self.config)
if url: w.set_url(url)
w.app = self.app
w.connect_slots(s)
diff --git a/lib/interface.py b/lib/interface.py
@@ -22,7 +22,7 @@ import threading, traceback, sys, time, json, Queue
from version import ELECTRUM_VERSION
from util import print_error
-from simple_config import SimpleConfig
+
DEFAULT_TIMEOUT = 5
DEFAULT_SERVERS = [ 'electrum.novit.ro:50001:t',
@@ -30,23 +30,10 @@ DEFAULT_SERVERS = [ 'electrum.novit.ro:50001:t',
proxy_modes = ['socks4', 'socks5', 'http']
-def replace_keys(obj, old_key, new_key):
- if isinstance(obj, dict):
- if old_key in obj:
- obj[new_key] = obj[old_key]
- del obj[old_key]
- for elem in obj.itervalues():
- replace_keys(elem, old_key, new_key)
- elif isinstance(obj, list):
- for elem in obj:
- replace_keys(elem, old_key, new_key)
-
-def old_to_new(d):
- replace_keys(d, 'blk_hash', 'block_hash')
- replace_keys(d, 'pos', 'index')
- replace_keys(d, 'nTime', 'timestamp')
- replace_keys(d, 'is_in', 'is_input')
- replace_keys(d, 'raw_scriptPubKey', 'raw_output_script')
+def pick_random_server():
+ print "using random server"
+ return random.choice( DEFAULT_SERVERS )
+
def parse_proxy_options(s):
if s.lower() == 'none': return None
@@ -65,7 +52,11 @@ def parse_proxy_options(s):
proxy["port"] = "8080" if proxy["mode"] == "http" else "1080"
return proxy
-class Interface(threading.Thread):
+
+
+
+class InterfaceAncestor(threading.Thread):
+
def __init__(self, host, port, proxy=None):
threading.Thread.__init__(self)
self.daemon = True
@@ -134,11 +125,11 @@ class Interface(threading.Thread):
-class PollingInterface(Interface):
+class PollingInterface(InterfaceAncestor):
""" non-persistent connection. synchronous calls"""
def __init__(self, host, port, proxy=None):
- Interface.__init__(self, host, port, proxy)
+ InterfaceAncestor.__init__(self, host, port, proxy)
self.session_id = None
def get_history(self, address):
@@ -146,14 +137,6 @@ class PollingInterface(Interface):
def poll(self):
pass
- #if is_new or wallet.remote_url:
- # self.was_updated = True
- # is_new = wallet.synchronize()
- # wallet.update_tx_history()
- # wallet.save()
- # return is_new
- #else:
- # return False
def run(self):
self.is_connected = True
@@ -249,11 +232,11 @@ class HttpStratumInterface(PollingInterface):
-class TcpStratumInterface(Interface):
+class TcpStratumInterface(InterfaceAncestor):
"""json-rpc over persistent TCP connection, asynchronous"""
def __init__(self, host, port, proxy=None):
- Interface.__init__(self, host, port, proxy)
+ InterfaceAncestor.__init__(self, host, port, proxy)
def init_socket(self):
global proxy_modes
@@ -328,38 +311,63 @@ class TcpStratumInterface(Interface):
+class Interface(TcpStratumInterface, HttpStratumInterface):
+
+ def __init__(self, config):
-
-class WalletSynchronizer(threading.Thread):
-
- def __init__(self, wallet, loop=False, servers_loaded_callback=None, proxy=None):
- threading.Thread.__init__(self)
- self.daemon = True
- self.wallet = wallet
- self.loop = loop
- self.proxy = proxy
- self.init_interface()
- self.servers_loaded_callback = servers_loaded_callback
-
- def init_interface(self):
try:
- host, port, protocol = self.wallet.server.split(':')
+ s = config.get('server')
+ host, port, protocol = s.split(':')
port = int(port)
except:
- self.wallet.pick_random_server()
- host, port, protocol = self.wallet.server.split(':')
+ s = pick_random_server()
+ host, port, protocol = s.split(':')
port = int(port)
+ proxy = config.get('proxy')
+ self.server = host + ':%d:%s'%(port, protocol)
+
#print protocol, host, port
if protocol == 't':
- InterfaceClass = TcpStratumInterface
+ TcpStratumInterface.__init__(self, host, port, proxy)
elif protocol == 'h':
- InterfaceClass = HttpStratumInterface
+ HttpStratumInterface.__init__(self, host, port, proxy)
else:
print_error("Error: Unknown protocol")
- InterfaceClass = TcpStratumInterface
+ TcpStratumInterface.__init__(self, host, port, proxy)
+
+
+ def set_server(self, server, proxy=None):
+ # raise an error if the format isnt correct
+ a,b,c = server.split(':')
+ b = int(b)
+ assert c in ['t', 'h']
+ # set the server
+ if server != self.server or proxy != self.proxy:
+ print "changing server:", server, proxy
+ self.server = server
+ self.proxy = proxy
+ self.is_connected = False # this exits the polling loop
+ self.poke()
+
+
+
+
+
+
+class WalletSynchronizer(threading.Thread):
- self.interface = InterfaceClass(host, port, self.proxy)
+ def __init__(self, wallet, config, loop=False, servers_loaded_callback=None):
+ threading.Thread.__init__(self)
+ self.daemon = True
+ self.wallet = wallet
+ self.loop = loop
+ self.config = config
+ self.init_interface()
+ self.servers_loaded_callback = servers_loaded_callback
+
+ def init_interface(self):
+ self.interface = Interface(self.config)
self.wallet.interface = self.interface
def handle_response(self, r):
diff --git a/lib/simple_config.py b/lib/simple_config.py
@@ -1,66 +1,176 @@
-import json
-import os
+import json, ast
+import os, ast
from util import user_dir
+from version import ELECTRUM_VERSION, SEED_VERSION
+from interface import parse_proxy_options
+
+
+# old stuff.. should be removed at some point
+def replace_keys(obj, old_key, new_key):
+ if isinstance(obj, dict):
+ if old_key in obj:
+ obj[new_key] = obj[old_key]
+ del obj[old_key]
+ for elem in obj.itervalues():
+ replace_keys(elem, old_key, new_key)
+ elif isinstance(obj, list):
+ for elem in obj:
+ replace_keys(elem, old_key, new_key)
+
+def old_to_new(d):
+ replace_keys(d, 'blk_hash', 'block_hash')
+ replace_keys(d, 'pos', 'index')
+ replace_keys(d, 'nTime', 'timestamp')
+ replace_keys(d, 'is_in', 'is_input')
+ replace_keys(d, 'raw_scriptPubKey', 'raw_output_script')
+
+
+
class SimpleConfig:
- default_options = {
- "gui": "lite",
- "proxy": None,
- "winpos-qt": [100, 100, 840, 400],
- "winpos-lite": [4, 25, 351, 149],
- "history": False
- }
-
- def __init__(self):
- # Find electrum data folder
- self.config_folder = user_dir()
- # Read the file
- if os.path.exists(self.config_file_path()):
- self.load_config()
+ def __init__(self, options):
+
+ self.wallet_config = {}
+ self.read_wallet_config(options.wallet_path)
+
+ self.common_config = {}
+ self.read_common_config()
+
+ self.options_config = {}
+
+ if options.server: self.options_config['server'] = options.server
+ if options.proxy: self.options_config['proxy'] = parse_proxy_options(options.proxy)
+ if options.gui: self.options_config['gui'] = options.gui
+
+
+
+ def set_key(self, key, value, save = False):
+ # find where a setting comes from and save it there
+ if self.options_config.get(key):
+ return
+
+ elif self.wallet_config.get(key):
+ self.wallet_config[key] = value
+ if save: self.save_wallet_config()
+
+ elif self.common_config.get(key):
+ self.common_config[key] = value
+ if save: self.save_common_config()
+
+ else:
+ # add key to wallet config
+ self.wallet_config[key] = value
+ if save: self.save_wallet_config()
+
+
+ def get(self, key, default=None):
+ # 1. command-line options always override everything
+ if self.options_config.has_key(key):
+ # print "found", key, "in options"
+ out = self.options_config.get(key)
+
+ # 2. configuration file overrides wallet file
+ elif self.common_config.has_key(key):
+ out = self.common_config.get(key)
+
else:
- self.config = self.default_options
- # Make config directory if it does not yet exist.
- if not os.path.exists(self.config_folder):
- os.mkdir(self.config_folder)
- self.save_config()
-
- # This is a friendly fallback to the old style default proxy options
- if(self.config.get("proxy") is not None and self.config["proxy"]["mode"] == "none"):
- self.set_key("proxy", None, True)
-
- def set_key(self, key, value, save = True):
- self.config[key] = value
- if save == True:
- self.save_config()
-
- def save_config(self):
- if not os.path.exists(self.config_folder):
- os.mkdir(self.config_folder)
- f = open(self.config_file_path(), "w+")
- f.write(json.dumps(self.config))
-
- def load_config(self):
- f = open(self.config_file_path(), "r")
- file_contents = f.read()
- if file_contents:
- user_config = json.loads(file_contents)
- for i in user_config:
- self.config[i] = user_config[i]
+ out = self.wallet_config.get(key)
+
+ if out is None and default is not None:
+ out = default
+ return out
+
+
+ def is_modifiable(self, key):
+ if self.options_config.has_key(key) or self.common_config.has_key(key):
+ return False
else:
- self.config = self.default_options
- self.save_config()
-
- def config_file_path(self):
- return "%s" % (self.config_folder + "/config.json")
-
- def __init__(self):
- # Find electrum data folder
- self.config_folder = user_dir()
- self.config = self.default_options
- # Read the file
- if os.path.exists(self.config_file_path()):
- self.load_config()
- self.save_config()
+ return True
+
+
+ def read_common_config(self):
+ for name in [ os.path.join( user_dir(), 'electrum.conf') , '/etc/electrum.conf']:
+ if os.path.exists(name):
+ from interface import parse_proxy_options
+ try:
+ import ConfigParser
+ except:
+ print "cannot parse electrum.conf. please install ConfigParser"
+ return
+
+ p = ConfigParser.ConfigParser()
+ p.read(name)
+ try:
+ self.common_config['server'] = p.get('interface','server')
+ except:
+ pass
+ try:
+ self.common_config['proxy'] = parse_proxy_options(p.get('interface','proxy'))
+ except:
+ pass
+ break
+
+
+
+ def init_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.
+ wallet_dir = user_dir()
+
+ # 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 save_common_config(self):
+ s = repr(self.common_config)
+ # todo: decide what to do
+ print "not saving settings in common config:", s
+
+
+
+ def read_wallet_config(self, path):
+ """Read the contents of the wallet file."""
+ self.wallet_file_exists = False
+ self.init_path(path)
+ try:
+ with open(self.path, "r") as f:
+ data = f.read()
+ except IOError:
+ return
+ try:
+ d = ast.literal_eval( data ) #parse raw data from reading wallet file
+ old_to_new(d)
+ except:
+ raise IOError("Cannot read wallet file.")
+
+ self.wallet_config = d
+ self.wallet_file_exists = True
+
+
+ def set_interface(self, interface):
+ pass
+
+ def set_gui(self, gui):
+ pass
+
+ def save(self):
+ self.save_wallet_config()
+
+ def save_wallet_config(self):
+ s = repr(self.wallet_config)
+ f = open(self.path,"w")
+ f.write( s )
+ f.close()
+ import stat
+ os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -273,49 +273,40 @@ def format_satoshis(x, is_diff=False, num_zeros = 0):
from version import ELECTRUM_VERSION, SEED_VERSION
-from interface import DEFAULT_SERVERS
-
class Wallet:
- def __init__(self):
+ def __init__(self, config={}):
+ self.config = config
self.electrum_version = ELECTRUM_VERSION
- self.seed_version = SEED_VERSION
self.update_callbacks = []
- self.gap_limit = 5 # configuration
- self.use_change = True
- self.fee = 100000
- self.num_zeros = 0
- self.master_public_key = ''
- self.conversion_currency = None
- self.theme = "Cleanlook"
-
# saved fields
- self.use_encryption = False
- self.addresses = [] # receiving addresses visible for user
- self.change_addresses = [] # addresses used as change
- self.seed = '' # encrypted
- self.history = {}
- self.labels = {} # labels for addresses and transactions
- self.aliases = {} # aliases for addresses
- self.authorities = {} # trusted addresses
- self.frozen_addresses = []
- self.prioritized_addresses = []
- self.gui_detailed_view = False
-
- self.receipts = {} # signed URIs
- self.receipt = None # next receipt
- self.addressbook = [] # outgoing addresses, for payments
- self.debug_server = False # write server communication debug info to stdout
+ self.seed_version = config.get('seed_version', SEED_VERSION)
+ self.gap_limit = config.get('gap_limit', 5)
+ self.use_change = config.get('use_change',True)
+ self.fee = int(config.get('fee',100000))
+ self.num_zeros = int(config.get('num_zeros',0))
+ self.master_public_key = config.get('master_public_key','').decode('hex')
+ self.use_encryption = config.get('use_encryption', False)
+ self.addresses = config.get('addresses', []) # receiving addresses visible for user
+ self.change_addresses = config.get('change_addresses', []) # addresses used as change
+ self.seed = config.get('seed', '') # encrypted
+ self.history = config.get('history',{})
+ self.labels = config.get('labels',{}) # labels for addresses and transactions
+ self.aliases = config.get('aliases', {}) # aliases for addresses
+ self.authorities = config.get('authorities', {}) # trusted addresses
+ self.frozen_addresses = config.get('frozen_addresses',[])
+ self.prioritized_addresses = config.get('prioritized_addresses',[])
+ self.receipts = config.get('receipts',{}) # signed URIs
+ self.addressbook = config.get('contacts', []) # outgoing addresses, for payments
+ self.imported_keys = config.get('imported_keys',{})
# not saved
+ self.receipt = None # next receipt
self.tx_history = {}
-
- self.imported_keys = {}
-
self.was_updated = True
self.blocks = -1
self.banner = ''
@@ -329,7 +320,10 @@ class Wallet:
self.lock = threading.Lock()
self.tx_event = threading.Event()
- self.pick_random_server()
+ self.update_tx_history()
+ if self.seed_version != SEED_VERSION:
+ raise ValueError("This wallet seed is deprecated. Please run upgrade.py for a diagnostic.")
+
def register_callback(self, update_callback):
with self.lock:
@@ -340,38 +334,9 @@ class Wallet:
callbacks = self.update_callbacks[:]
[update() for update in callbacks]
- def pick_random_server(self):
- self.server = random.choice( DEFAULT_SERVERS ) # random choice when the wallet is created
-
def is_up_to_date(self):
return self.interface.responses.empty() and not self.interface.unanswered_requests
- def set_server(self, server, proxy=None):
- # raise an error if the format isnt correct
- a,b,c = server.split(':')
- b = int(b)
- assert c in ['t', 'h', 'n']
- # set the server
- if server != self.server or proxy != self.interface.proxy:
- self.server = server
- self.save()
- self.interface.proxy = proxy
- self.interface.is_connected = False # this exits the polling loop
- 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.
- wallet_dir = user_dir()
-
- # 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(':')
@@ -390,6 +355,7 @@ class Wallet:
raise BaseException('Address does not match private key')
self.imported_keys[address] = self.pw_encode( key, password )
+
def new_seed(self, password):
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
#self.init_mpk(seed)
@@ -630,91 +596,6 @@ class Wallet:
self.update_tx_labels()
- def save(self):
- # TODO: Need special config storage class. Should not be mixed
- # up with the wallet.
- # Settings should maybe be stored in a flat ini file.
- s = {
- 'seed_version': self.seed_version,
- 'use_encryption': self.use_encryption,
- 'use_change': self.use_change,
- 'master_public_key': self.master_public_key.encode('hex'),
- 'fee': self.fee,
- 'server': self.server,
- 'seed': self.seed,
- 'addresses': self.addresses,
- 'change_addresses': self.change_addresses,
- 'history': self.history,
- 'labels': self.labels,
- 'contacts': self.addressbook,
- 'imported_keys': self.imported_keys,
- 'aliases': self.aliases,
- 'authorities': self.authorities,
- 'receipts': self.receipts,
- 'num_zeros': self.num_zeros,
- 'frozen_addresses': self.frozen_addresses,
- 'prioritized_addresses': self.prioritized_addresses,
- 'gui_detailed_view': self.gui_detailed_view,
- 'gap_limit': self.gap_limit,
- 'debug_server': self.debug_server,
- 'conversion_currency': self.conversion_currency,
- 'theme': self.theme
- }
- f = open(self.path,"w")
- f.write( repr(s) )
- f.close()
- import stat
- os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
-
- def read(self):
- """Read the contents of the wallet file."""
- import interface
-
- self.file_exists = False
- try:
- with open(self.path, "r") as f:
- data = f.read()
- except IOError:
- return
- try:
- 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')
- self.use_change = bool(d.get('use_change', True))
- self.fee = int(d.get('fee'))
- self.seed = d.get('seed')
- self.server = d.get('server')
- self.addresses = d.get('addresses')
- self.change_addresses = d.get('change_addresses')
- self.history = d.get('history')
- self.labels = d.get('labels')
- self.addressbook = d.get('contacts')
- self.imported_keys = d.get('imported_keys', {})
- self.aliases = d.get('aliases', {})
- self.authorities = d.get('authorities', {})
- self.receipts = d.get('receipts', {})
- self.num_zeros = d.get('num_zeros', 0)
- self.frozen_addresses = d.get('frozen_addresses', [])
- self.prioritized_addresses = d.get('prioritized_addresses', [])
- self.gui_detailed_view = d.get('gui_detailed_view', False)
- self.gap_limit = d.get('gap_limit', 5)
- self.debug_server = d.get('debug_server', False)
- self.conversion_currency = d.get('conversion_currency', 'USD')
- self.theme = d.get('theme', 'Cleanlook')
- except:
- raise IOError("Cannot read wallet file.")
-
- self.update_tx_history()
-
- if self.seed_version != SEED_VERSION:
- raise ValueError("This wallet seed is deprecated. Please run upgrade.py for a diagnostic.")
-
- self.file_exists = True
-
-
def get_address_flags(self, addr):
flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-"
flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
@@ -1134,3 +1015,29 @@ class Wallet:
return True
else:
return False
+
+ def save(self):
+ s = {
+ 'seed_version': self.seed_version,
+ 'use_encryption': self.use_encryption,
+ 'use_change': self.use_change,
+ 'master_public_key': self.master_public_key.encode('hex'),
+ 'fee': self.fee,
+ 'seed': self.seed,
+ 'addresses': self.addresses,
+ 'change_addresses': self.change_addresses,
+ 'history': self.history,
+ 'labels': self.labels,
+ 'contacts': self.addressbook,
+ 'imported_keys': self.imported_keys,
+ 'aliases': self.aliases,
+ 'authorities': self.authorities,
+ 'receipts': self.receipts,
+ 'num_zeros': self.num_zeros,
+ 'frozen_addresses': self.frozen_addresses,
+ 'prioritized_addresses': self.prioritized_addresses,
+ 'gap_limit': self.gap_limit,
+ }
+ for k, v in s.items():
+ self.config.set_key(k,v)
+ self.config.save()
diff --git a/scripts/blocks b/scripts/blocks
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-from electrum import TcpStratumInterface
+from electrum import Interface
-i = TcpStratumInterface('electrum.novit.ro', 50001)
+i = Interface({'server':'electrum.novit.ro:50001:t'})
i.init_socket()
i.start()
i.send([('blockchain.numblocks.subscribe',[])])
diff --git a/scripts/get_history b/scripts/get_history
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from electrum import TcpStratumInterface
+from electrum import Interface
try:
addr = sys.argv[1]
@@ -9,7 +9,7 @@ except:
print "usage: get_history <bitcoin_address>"
sys.exit(1)
-i = TcpStratumInterface('electrum.novit.ro', 50001)
+i = Interface({'server':'electrum.novit.ro:50001:t'})
i.init_socket()
i.start()
i.send([('blockchain.address.get_history',[addr])])
diff --git a/scripts/merchant.py b/scripts/merchant.py
@@ -21,7 +21,7 @@ import time, thread, sys, socket, os
import urllib2,json
import MySQLdb as mdb
import Queue
-from electrum import Wallet, TcpStratumInterface
+from electrum import Wallet, Interface
import ConfigParser
config = ConfigParser.ConfigParser()
@@ -157,7 +157,7 @@ if __name__ == '__main__':
print "using database", db_name
conn = mdb.connect(db_instance, db_user, db_password, db_name);
- i = TcpStratumInterface(electrum_server, 50001)
+ i = Interface({'server':"%s:%d:t"%(electrum_server, 50001)})
i.init_socket()
i.start()
diff --git a/scripts/peers b/scripts/peers
@@ -1,8 +1,8 @@
#!/usr/bin/env python
-from electrum import TcpStratumInterface
+from electrum import Interface
-i = TcpStratumInterface('electrum.novit.ro', 50001)
+i = Interface({'server':'electrum.novit.ro:50001:t'})
i.init_socket()
i.start()
i.send([('server.peers.subscribe',[])])
diff --git a/scripts/watch_address b/scripts/watch_address
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import sys
-from electrum import TcpStratumInterface
+from electrum import Interface
try:
addr = sys.argv[1]
@@ -9,7 +9,7 @@ except:
print "usage: watch_address <bitcoin_address>"
sys.exit(1)
-i = TcpStratumInterface('electrum.novit.ro', 50001)
+i = Interface({'server':'electrum.novit.ro:50001:t'})
i.init_socket()
i.start()
i.send([('blockchain.address.subscribe',[addr])])