Browse Source

big refactoring: command line options and electrum.conf options override settings in wallet file.

283
thomasv 12 years ago
parent
commit
5444f55e6b
  1. 63
      electrum
  2. 3
      lib/__init__.py
  3. 10
      lib/gui.py
  4. 46
      lib/gui_lite.py
  5. 81
      lib/gui_qt.py
  6. 110
      lib/interface.py
  7. 226
      lib/simple_config.py
  8. 199
      lib/wallet.py
  9. 4
      scripts/blocks
  10. 4
      scripts/get_history
  11. 4
      scripts/merchant.py
  12. 4
      scripts/peers
  13. 4
      scripts/watch_address

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

3
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

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

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

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

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

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

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

4
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',[])])

4
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])])

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

4
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',[])])

4
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])])

Loading…
Cancel
Save