Browse Source

Merge the network and network_proxy

283
Neil Booth 10 years ago
parent
commit
2d05e7d891
  1. 30
      electrum
  2. 2
      gui/qt/main_window.py
  3. 1
      lib/__init__.py
  4. 19
      lib/commands.py
  5. 137
      lib/network.py
  6. 235
      lib/network_proxy.py
  7. 2
      lib/transaction.py
  8. 6
      lib/verifier.py
  9. 6
      plugins/keepkey.py
  10. 6
      plugins/trezor.py
  11. 4
      scripts/block_headers
  12. 9
      scripts/get_history
  13. 20
      scripts/merchant/merchant.py
  14. 4
      scripts/watch_address

30
electrum

@ -77,7 +77,7 @@ if is_bundle or is_local or is_android:
from electrum import util from electrum import util
from electrum import SimpleConfig, Network, Wallet, WalletStorage, NetworkProxy from electrum import SimpleConfig, Network, Wallet, WalletStorage
from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
from electrum.plugins import init_plugins, run_hook, always_hook from electrum.plugins import init_plugins, run_hook, always_hook
from electrum.commands import get_parser, known_commands, Commands, config_variables from electrum.commands import get_parser, known_commands, Commands, config_variables
@ -97,12 +97,12 @@ def prompt_password(prompt, confirm=True):
def init_gui(config, network_proxy): def init_gui(config, network):
gui_name = config.get('gui', 'qt') gui_name = config.get('gui', 'qt')
if gui_name in ['lite', 'classic']: if gui_name in ['lite', 'classic']:
gui_name = 'qt' gui_name = 'qt'
gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui']) gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
gui = gui.ElectrumGui(config, network_proxy) gui = gui.ElectrumGui(config, network)
return gui return gui
@ -157,8 +157,7 @@ def init_cmdline(config):
wallet = Wallet.from_seed(seed, password, storage) wallet = Wallet.from_seed(seed, password, storage)
if not config.get('offline'): if not config.get('offline'):
s = get_daemon(config, False) network = Network(config)
network = NetworkProxy(s, config)
network.start() network.start()
wallet.start_threads(network) wallet.start_threads(network)
print_msg("Recovering wallet...") print_msg("Recovering wallet...")
@ -326,19 +325,18 @@ class ClientThread(util.DaemonThread):
# send response and exit # send response and exit
self.client_pipe.send(response) self.client_pipe.send(response)
self.server.remove_client(self) self.server.remove_client(self)
class NetworkServer(util.DaemonThread): class NetworkServer(util.DaemonThread):
def __init__(self, config, network_proxy): def __init__(self, config, network):
util.DaemonThread.__init__(self) util.DaemonThread.__init__(self)
self.debug = False self.debug = False
self.config = config self.config = config
self.pipe = util.QueuePipe() self.pipe = util.QueuePipe()
self.network_proxy = network_proxy self.network = network
self.network = self.network_proxy.network
self.lock = threading.RLock() self.lock = threading.RLock()
# each GUI is a client of the daemon # each GUI is a client of the daemon
self.clients = [] self.clients = []
@ -516,11 +514,11 @@ if __name__ == '__main__':
# daemon is not running # daemon is not running
if cmd_name == 'gui': if cmd_name == 'gui':
network_proxy = NetworkProxy(None, config) network = Network(config)
network_proxy.start() network.start()
server = NetworkServer(config, network_proxy) server = NetworkServer(config, network)
server.start() server.start()
server.gui = init_gui(config, network_proxy) server.gui = init_gui(config, network)
server.gui.main() server.gui.main()
elif cmd_name == 'daemon': elif cmd_name == 'daemon':
subcommand = config.get('subcommand') subcommand = config.get('subcommand')
@ -530,9 +528,9 @@ if __name__ == '__main__':
elif subcommand == 'start': elif subcommand == 'start':
p = os.fork() p = os.fork()
if p == 0: if p == 0:
network_proxy = NetworkProxy(None, config) network = Network(config)
network_proxy.start() network.start()
server = NetworkServer(config, network_proxy) server = NetworkServer(config, network)
if config.get('websocket_server'): if config.get('websocket_server'):
import websockets import websockets
websockets.WebSocketServer(config, server).start() websockets.WebSocketServer(config, server).start()

2
gui/qt/main_window.py

@ -2357,7 +2357,7 @@ class ElectrumWindow(QMainWindow):
txid, ok = QInputDialog.getText(self, _('Lookup transaction'), _('Transaction ID') + ':') txid, ok = QInputDialog.getText(self, _('Lookup transaction'), _('Transaction ID') + ':')
if ok and txid: if ok and txid:
try: try:
r = self.network.synchronous_get([('blockchain.transaction.get',[str(txid)])])[0] r = self.network.synchronous_get(('blockchain.transaction.get',[str(txid)]))
except BaseException as e: except BaseException as e:
self.show_message(str(e)) self.show_message(str(e))
return return

1
lib/__init__.py

@ -11,4 +11,3 @@ import transaction
from transaction import Transaction from transaction import Transaction
from plugins import BasePlugin from plugins import BasePlugin
from commands import Commands, known_commands from commands import Commands, known_commands
from network_proxy import NetworkProxy

19
lib/commands.py

@ -148,7 +148,7 @@ class Commands:
"""Return the transaction history of any address. Note: This is a """Return the transaction history of any address. Note: This is a
walletless server query, results are not checked by SPV. walletless server query, results are not checked by SPV.
""" """
return self.network.synchronous_get([('blockchain.address.get_history', [address])])[0] return self.network.synchronous_get(('blockchain.address.get_history', [address]))
@command('nw') @command('nw')
def listunspent(self): def listunspent(self):
@ -165,16 +165,15 @@ class Commands:
"""Returns the UTXO list of any address. Note: This """Returns the UTXO list of any address. Note: This
is a walletless server query, results are not checked by SPV. is a walletless server query, results are not checked by SPV.
""" """
return self.network.synchronous_get([('blockchain.address.listunspent', [address])])[0] return self.network.synchronous_get(('blockchain.address.listunspent', [address]))
@command('n') @command('n')
def getutxoaddress(self, txid, pos): def getutxoaddress(self, txid, pos):
"""Get the address of a UTXO. Note: This is a walletless server query, results are """Get the address of a UTXO. Note: This is a walletless server query, results are
not checked by SPV. not checked by SPV.
""" """
r = self.network.synchronous_get([('blockchain.utxo.get_address', [txid, pos])]) r = self.network.synchronous_get(('blockchain.utxo.get_address', [txid, pos]))
if r: return {'address': r}
return {'address':r[0]}
@command('wp') @command('wp')
def createrawtx(self, inputs, outputs, unsigned=False): def createrawtx(self, inputs, outputs, unsigned=False):
@ -219,7 +218,7 @@ class Commands:
def broadcast(self, tx): def broadcast(self, tx):
"""Broadcast a transaction to the network. """ """Broadcast a transaction to the network. """
t = Transaction(tx) t = Transaction(tx)
return self.network.synchronous_get([('blockchain.transaction.broadcast', [str(t)])])[0] return self.network.synchronous_get(('blockchain.transaction.broadcast', [str(t)]))
@command('') @command('')
def createmultisig(self, num, pubkeys): def createmultisig(self, num, pubkeys):
@ -287,7 +286,7 @@ class Commands:
"""Return the balance of any address. Note: This is a walletless """Return the balance of any address. Note: This is a walletless
server query, results are not checked by SPV. server query, results are not checked by SPV.
""" """
out = self.network.synchronous_get([('blockchain.address.get_balance', [address])])[0] out = self.network.synchronous_get(('blockchain.address.get_balance', [address]))
out["confirmed"] = str(Decimal(out["confirmed"])/COIN) out["confirmed"] = str(Decimal(out["confirmed"])/COIN)
out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN) out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN)
return out return out
@ -295,7 +294,7 @@ class Commands:
@command('n') @command('n')
def getproof(self, address): def getproof(self, address):
"""Get Merkle branch of an address in the UTXO set""" """Get Merkle branch of an address in the UTXO set"""
p = self.network.synchronous_get([('blockchain.address.get_proof', [address])])[0] p = self.network.synchronous_get(('blockchain.address.get_proof', [address]))
out = [] out = []
for i,s in p: for i,s in p:
out.append(i) out.append(i)
@ -305,7 +304,7 @@ class Commands:
def getmerkle(self, txid, height): def getmerkle(self, txid, height):
"""Get Merkle branch of a transaction included in a block. Electrum """Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions (Simple Payment Verification).""" uses this to verify transactions (Simple Payment Verification)."""
return self.network.synchronous_get([('blockchain.transaction.get_merkle', [txid, int(height)])])[0] return self.network.synchronous_get(('blockchain.transaction.get_merkle', [txid, int(height)]))
@command('n') @command('n')
def getservers(self): def getservers(self):
@ -522,7 +521,7 @@ class Commands:
"""Retrieve a transaction. """ """Retrieve a transaction. """
tx = self.wallet.transactions.get(txid) if self.wallet else None tx = self.wallet.transactions.get(txid) if self.wallet else None
if tx is None and self.network: if tx is None and self.network:
raw = self.network.synchronous_get([('blockchain.transaction.get', [txid])])[0] raw = self.network.synchronous_get(('blockchain.transaction.get', [txid]))
if raw: if raw:
tx = Transaction(raw) tx = Transaction(raw)
else: else:

137
lib/network.py

@ -5,7 +5,8 @@ import sys
import random import random
import select import select
import traceback import traceback
from collections import deque from collections import defaultdict, deque
from threading import Lock
import socks import socks
import socket import socket
@ -129,20 +130,19 @@ class Network(util.DaemonThread):
Our external API: Our external API:
- Member functions get_header(), get_parameters(), get_status_value(), - Member functions get_header(), get_interfaces(), get_local_height(),
new_blockchain_height(), set_parameters(), start(), get_parameters(), get_server_height(), get_status_value(),
stop() is_connected(), new_blockchain_height(), set_parameters(), start(),
stop()
""" """
def __init__(self, pipe, config=None): def __init__(self, config=None):
if config is None: if config is None:
config = {} # Do not use mutables as default values! config = {} # Do not use mutables as default values!
util.DaemonThread.__init__(self) util.DaemonThread.__init__(self)
self.config = SimpleConfig(config) if type(config) == type({}) else config self.config = SimpleConfig(config) if type(config) == type({}) else config
self.num_server = 8 if not self.config.get('oneserver') else 0 self.num_server = 8 if not self.config.get('oneserver') else 0
self.blockchain = Blockchain(self.config, self) self.blockchain = Blockchain(self.config, self)
self.requests_queue = pipe.send_queue
self.response_queue = pipe.get_queue
# A deque of interface header requests, processed left-to-right # A deque of interface header requests, processed left-to-right
self.bc_requests = deque() self.bc_requests = deque()
# Server for addresses and transactions # Server for addresses and transactions
@ -155,6 +155,10 @@ class Network(util.DaemonThread):
if not self.default_server: if not self.default_server:
self.default_server = pick_random_server() self.default_server = pick_random_server()
self.lock = Lock()
self.pending_sends = []
self.message_id = 0
self.debug = False
self.irc_servers = {} # returned by interface (list from irc) self.irc_servers = {} # returned by interface (list from irc)
self.recent_servers = self.read_recent_servers() self.recent_servers = self.read_recent_servers()
@ -163,6 +167,8 @@ class Network(util.DaemonThread):
self.heights = {} self.heights = {}
self.merkle_roots = {} self.merkle_roots = {}
self.utxo_roots = {} self.utxo_roots = {}
self.subscriptions = defaultdict(list)
self.callbacks = defaultdict(list)
dir_path = os.path.join( self.config.path, 'certs') dir_path = os.path.join( self.config.path, 'certs')
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
@ -188,6 +194,15 @@ class Network(util.DaemonThread):
self.start_network(deserialize_server(self.default_server)[2], self.start_network(deserialize_server(self.default_server)[2],
deserialize_proxy(self.config.get('proxy'))) deserialize_proxy(self.config.get('proxy')))
def register_callback(self, event, callback):
with self.lock:
self.callbacks[event].append(callback)
def trigger_callback(self, event, params=()):
with self.lock:
callbacks = self.callbacks[event][:]
[callback(*params) for callback in callbacks]
def read_recent_servers(self): def read_recent_servers(self):
if not self.config.path: if not self.config.path:
return [] return []
@ -231,6 +246,12 @@ class Network(util.DaemonThread):
def is_connected(self): def is_connected(self):
return self.interface is not None return self.interface is not None
def is_connecting(self):
return self.connection_status == 'connecting'
def is_up_to_date(self):
return self.unanswered_requests == {}
def queue_request(self, method, params): def queue_request(self, method, params):
self.interface.queue_request({'method': method, 'params': params}) self.interface.queue_request({'method': method, 'params': params})
@ -263,7 +284,10 @@ class Network(util.DaemonThread):
def notify(self, key): def notify(self, key):
value = self.get_status_value(key) value = self.get_status_value(key)
self.response_queue.put({'method':'network.status', 'params':[key, value]}) if key in ['status', 'updated']:
self.trigger_callback(key)
else:
self.trigger_callback(key, (value,))
def get_parameters(self): def get_parameters(self):
host, port, protocol = deserialize_server(self.default_server) host, port, protocol = deserialize_server(self.default_server)
@ -337,8 +361,16 @@ class Network(util.DaemonThread):
self.socket_queue = Queue.Queue() self.socket_queue = Queue.Queue()
def set_parameters(self, host, port, protocol, proxy, auto_connect): def set_parameters(self, host, port, protocol, proxy, auto_connect):
self.auto_connect = auto_connect proxy_str = serialize_proxy(proxy)
server = serialize_server(host, port, protocol) server = serialize_server(host, port, protocol)
self.config.set_key('auto_connect', auto_connect, False)
self.config.set_key("proxy", proxy_str, False)
self.config.set_key("server", server, True)
# abort if changes were not allowed by config
if self.config.get('server') != server_str or self.config.get('proxy') != proxy_str:
return
self.auto_connect = auto_connect
if self.proxy != proxy or self.protocol != protocol: if self.proxy != proxy or self.protocol != protocol:
# Restart the network defaulting to the given server # Restart the network defaulting to the given server
self.stop_network() self.stop_network()
@ -405,7 +437,9 @@ class Network(util.DaemonThread):
self.switch_lagging_interface(i.server) self.switch_lagging_interface(i.server)
self.notify('updated') self.notify('updated')
def process_response(self, interface, response): def process_response(self, interface, response, callback):
if self.debug:
self.print_error("<--", response)
error = response.get('error') error = response.get('error')
result = response.get('result') result = response.get('result')
method = response.get('method') method = response.get('method')
@ -437,8 +471,19 @@ class Network(util.DaemonThread):
# Cache address subscription results # Cache address subscription results
if method == 'blockchain.address.subscribe' and error is None: if method == 'blockchain.address.subscribe' and error is None:
addr = response['params'][0] addr = response['params'][0]
self.addr_responses[addr] = result self.addr_responses[addr] = response
self.response_queue.put(response) if callback is None:
params = response['params']
with self.lock:
for k,v in self.subscriptions.items():
if (method, params) in v:
callback = k
break
if callback is None:
self.print_error("received unexpected notification",
method, params)
else:
callback(response)
def process_responses(self, interface): def process_responses(self, interface):
notifications, responses = interface.get_responses() notifications, responses = interface.get_responses()
@ -449,12 +494,14 @@ class Network(util.DaemonThread):
if client_id is not None: if client_id is not None:
if interface != self.interface: if interface != self.interface:
continue continue
self.unanswered_requests.pop(client_id) _req, callback = self.unanswered_requests.pop(client_id)
else:
callback = None
# Copy the request method and params to the response # Copy the request method and params to the response
response['method'] = request.get('method') response['method'] = request.get('method')
response['params'] = request.get('params') response['params'] = request.get('params')
response['id'] = client_id response['id'] = client_id
self.process_response(interface, response) self.process_response(interface, response, callback)
for response in notifications: for response in notifications:
if not response: # Closed remotely if not response: # Closed remotely
@ -466,16 +513,42 @@ class Network(util.DaemonThread):
response['result'] = response['params'][0] response['result'] = response['params'][0]
response['params'] = [] response['params'] = []
elif method == 'blockchain.address.subscribe': elif method == 'blockchain.address.subscribe':
params = response['params']
response['params'] = [params[0]] # addr response['params'] = [params[0]] # addr
response['result'] = params[1] response['result'] = params[1]
self.process_response(interface, response) self.process_response(interface, response, None)
def handle_incoming_requests(self): def send(self, messages, callback):
while not self.requests_queue.empty(): '''Messages is a list of (method, value) tuples'''
self.process_request(self.requests_queue.get()) with self.lock:
self.pending_sends.append((messages, callback))
def process_request(self, request):
def process_pending_sends(self):
sends = self.pending_sends
self.pending_sends = []
for messages, callback in sends:
subs = filter(lambda (m,v): m.endswith('.subscribe'), messages)
with self.lock:
for sub in subs:
if sub not in self.subscriptions[callback]:
self.subscriptions[callback].append(sub)
_id = self.message_id
self.message_id += len(messages)
unsent = []
for message in messages:
method, params = message
request = {'id': _id, 'method': method, 'params': params}
if not self.process_request(request, callback):
unsent.append(message)
_id += 1
if unsent:
with self.lock:
self.pending_sends.append((unsent, callback))
# FIXME: inline this function
def process_request(self, request, callback):
'''Returns true if the request was processed.''' '''Returns true if the request was processed.'''
method = request['method'] method = request['method']
params = request['params'] params = request['params']
@ -492,14 +565,14 @@ class Network(util.DaemonThread):
out['error'] = str(e) out['error'] = str(e)
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
self.print_error("network error", str(e)) self.print_error("network error", str(e))
self.response_queue.put(out) callback(out)
return True return True
if method == 'blockchain.address.subscribe': if method == 'blockchain.address.subscribe':
addr = params[0] addr = params[0]
self.subscribed_addresses.add(addr) self.subscribed_addresses.add(addr)
if addr in self.addr_responses: if addr in self.addr_responses:
self.response_queue.put({'id':_id, 'result':self.addr_responses[addr]}) callback(self.addr_responses[addr])
return True return True
# This request needs connectivity. If we don't have an # This request needs connectivity. If we don't have an
@ -507,7 +580,9 @@ class Network(util.DaemonThread):
if not self.interface: if not self.interface:
return False return False
self.unanswered_requests[_id] = request if self.debug:
self.print_error("-->", request)
self.unanswered_requests[_id] = request, callback
self.interface.queue_request(request) self.interface.queue_request(request)
return True return True
@ -679,10 +754,12 @@ class Network(util.DaemonThread):
while self.is_running(): while self.is_running():
self.maintain_sockets() self.maintain_sockets()
self.wait_on_sockets() self.wait_on_sockets()
self.handle_incoming_requests()
self.handle_bc_requests() self.handle_bc_requests()
self.run_jobs() # Synchronizer and Verifier
self.process_pending_sends()
self.stop_network() self.stop_network()
self.trigger_callback('stop')
self.print_error("stopped") self.print_error("stopped")
def on_header(self, i, header): def on_header(self, i, header):
@ -706,3 +783,11 @@ class Network(util.DaemonThread):
def get_local_height(self): def get_local_height(self):
return self.blockchain.height() return self.blockchain.height()
def synchronous_get(self, request, timeout=100000000):
queue = Queue.Queue()
self.send([request], queue.put)
r = queue.get(True, timeout)
if r.get('error'):
raise BaseException(r.get('error'))
return r.get('result')

235
lib/network_proxy.py

@ -1,235 +0,0 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2014 Thomas Voegtlin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import traceback
import threading
import Queue
import util
from network import Network, serialize_proxy, serialize_server
from simple_config import SimpleConfig
class NetworkProxy(util.DaemonThread):
def __init__(self, socket, config=None):
if config is None:
config = {} # Do not use mutables as default arguments!
util.DaemonThread.__init__(self)
self.config = SimpleConfig(config) if type(config) == type({}) else config
self.message_id = 0
self.unanswered_requests = {}
self.subscriptions = {}
self.debug = False
self.lock = threading.Lock()
self.callbacks = {}
if socket:
self.pipe = util.SocketPipe(socket)
self.network = None
else:
self.pipe = util.QueuePipe()
self.network = Network(self.pipe, config)
self.network.start()
for key in ['fee','status','banner','updated','servers','interfaces']:
value = self.network.get_status_value(key)
self.pipe.get_queue.put({'method':'network.status', 'params':[key, value]})
# status variables
self.status = 'unknown'
self.servers = {}
self.banner = ''
self.blockchain_height = 0
self.server_height = 0
self.interfaces = []
# value returned by estimatefee
self.fee = None
def run(self):
while self.is_running():
self.run_jobs() # Synchronizer and Verifier
try:
response = self.pipe.get()
except util.timeout:
continue
if response is None:
break
# Protect against ill-formed or malicious server responses
try:
self.process(response)
except:
traceback.print_exc(file=sys.stderr)
self.trigger_callback('stop')
if self.network:
self.network.stop()
self.print_error("stopped")
def process(self, response):
if self.debug:
self.print_error("<--", response)
if response.get('method') == 'network.status':
key, value = response.get('params')
if key == 'status':
self.status = value
elif key == 'banner':
self.banner = value
elif key == 'fee':
self.fee = value
elif key == 'updated':
self.blockchain_height, self.server_height = value
elif key == 'servers':
self.servers = value
elif key == 'interfaces':
self.interfaces = value
if key in ['status', 'updated']:
self.trigger_callback(key)
else:
self.trigger_callback(key, (value,))
return
msg_id = response.get('id')
result = response.get('result')
error = response.get('error')
if msg_id is not None:
with self.lock:
method, params, callback = self.unanswered_requests.pop(msg_id)
else:
method = response.get('method')
params = response.get('params')
with self.lock:
for k,v in self.subscriptions.items():
if (method, params) in v:
callback = k
break
else:
self.print_error("received unexpected notification",
method, params)
return
r = {'method':method, 'params':params, 'result':result,
'id':msg_id, 'error':error}
callback(r)
def send(self, messages, callback):
"""return the ids of the requests that we sent"""
# detect subscriptions
sub = []
for message in messages:
m, v = message
if m[-10:] == '.subscribe':
sub.append(message)
if sub:
with self.lock:
if self.subscriptions.get(callback) is None:
self.subscriptions[callback] = []
for message in sub:
if message not in self.subscriptions[callback]:
self.subscriptions[callback].append(message)
with self.lock:
requests = []
ids = []
for m in messages:
method, params = m
request = { 'id':self.message_id, 'method':method, 'params':params }
self.unanswered_requests[self.message_id] = method, params, callback
ids.append(self.message_id)
requests.append(request)
if self.debug:
self.print_error("-->", request)
self.message_id += 1
self.pipe.send_all(requests)
return ids
def synchronous_get(self, requests, timeout=100000000):
queue = Queue.Queue()
ids = self.send(requests, queue.put)
id2 = ids[:]
res = {}
while ids:
r = queue.get(True, timeout)
_id = r.get('id')
ids.remove(_id)
if r.get('error'):
raise BaseException(r.get('error'))
result = r.get('result')
res[_id] = r.get('result')
out = []
for _id in id2:
out.append(res[_id])
return out
def get_servers(self):
return self.servers
def get_interfaces(self):
return self.interfaces
def get_local_height(self):
return self.blockchain_height
def get_server_height(self):
return self.server_height
def is_connected(self):
return self.status == 'connected'
def is_connecting(self):
return self.status == 'connecting'
def is_up_to_date(self):
return self.unanswered_requests == {}
def get_parameters(self):
return self.synchronous_get([('network.get_parameters', [])])[0]
def set_parameters(self, host, port, protocol, proxy, auto_connect):
proxy_str = serialize_proxy(proxy)
server_str = serialize_server(host, port, protocol)
self.config.set_key('auto_connect', auto_connect, False)
self.config.set_key("proxy", proxy_str, False)
self.config.set_key("server", server_str, True)
# abort if changes were not allowed by config
if self.config.get('server') != server_str or self.config.get('proxy') != proxy_str:
return
return self.synchronous_get([('network.set_parameters', (host, port, protocol, proxy, auto_connect))])[0]
def stop_daemon(self):
return self.send([('daemon.stop',[])], None)
def register_callback(self, event, callback):
with self.lock:
if not self.callbacks.get(event):
self.callbacks[event] = []
self.callbacks[event].append(callback)
def trigger_callback(self, event, params=()):
with self.lock:
callbacks = self.callbacks.get(event,[])[:]
if callbacks:
[callback(*params) for callback in callbacks]

2
lib/transaction.py

@ -545,7 +545,7 @@ class Transaction:
for privkey in privkeys: for privkey in privkeys:
pubkey = public_key_from_private_key(privkey) pubkey = public_key_from_private_key(privkey)
address = address_from_private_key(privkey) address = address_from_private_key(privkey)
u = network.synchronous_get([ ('blockchain.address.listunspent',[address])])[0] u = network.synchronous_get(('blockchain.address.listunspent',[address]))
pay_script = klass.pay_script('address', address) pay_script = klass.pay_script('address', address)
for item in u: for item in u:
item['scriptPubKey'] = pay_script item['scriptPubKey'] = pay_script

6
lib/verifier.py

@ -40,9 +40,9 @@ class SPV(ThreadJob):
if tx_hash not in self.merkle_roots and tx_height <= lh: if tx_hash not in self.merkle_roots and tx_height <= lh:
request = ('blockchain.transaction.get_merkle', request = ('blockchain.transaction.get_merkle',
[tx_hash, tx_height]) [tx_hash, tx_height])
if self.network.send([request], self.merkle_response): self.network.send([request], self.merkle_response)
self.print_error('requested merkle', tx_hash) self.print_error('requested merkle', tx_hash)
self.merkle_roots[tx_hash] = None self.merkle_roots[tx_hash] = None
def merkle_response(self, r): def merkle_response(self, r):
if r.get('error'): if r.get('error'):

6
plugins/keepkey.py

@ -156,7 +156,7 @@ class Plugin(BasePlugin):
@hook @hook
def installwizard_restore(self, wizard, storage): def installwizard_restore(self, wizard, storage):
if storage.get('wallet_type') != 'keepkey': if storage.get('wallet_type') != 'keepkey':
return return
seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True) seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True)
if not seed: if not seed:
@ -478,7 +478,7 @@ class KeepKeyWallet(BIP32_HD_Wallet):
ptx = self.transactions.get(tx_hash) ptx = self.transactions.get(tx_hash)
if ptx is None: if ptx is None:
ptx = self.network.synchronous_get([('blockchain.transaction.get', [tx_hash])])[0] ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
ptx = Transaction(ptx) ptx = Transaction(ptx)
prev_tx[tx_hash] = ptx prev_tx[tx_hash] = ptx
@ -673,5 +673,5 @@ if KEEPKEY:
except ConnectionError: except ConnectionError:
self.bad = True self.bad = True
raise raise
return resp return resp

6
plugins/trezor.py

@ -156,7 +156,7 @@ class Plugin(BasePlugin):
@hook @hook
def installwizard_restore(self, wizard, storage): def installwizard_restore(self, wizard, storage):
if storage.get('wallet_type') != 'trezor': if storage.get('wallet_type') != 'trezor':
return return
seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True) seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
if not seed: if not seed:
@ -477,7 +477,7 @@ class TrezorWallet(BIP32_HD_Wallet):
ptx = self.transactions.get(tx_hash) ptx = self.transactions.get(tx_hash)
if ptx is None: if ptx is None:
ptx = self.network.synchronous_get([('blockchain.transaction.get', [tx_hash])])[0] ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
ptx = Transaction(ptx) ptx = Transaction(ptx)
prev_tx[tx_hash] = ptx prev_tx[tx_hash] = ptx
@ -665,5 +665,5 @@ if TREZOR:
except ConnectionError: except ConnectionError:
self.bad = True self.bad = True
raise raise
return resp return resp

4
scripts/block_headers

@ -7,8 +7,7 @@ import electrum
# start network # start network
c = electrum.SimpleConfig() c = electrum.SimpleConfig()
s = electrum.daemon.get_daemon(c,True) network = electrum.Network(c)
network = electrum.NetworkProxy(s,c)
network.start() network.start()
# wait until connected # wait until connected
@ -26,4 +25,3 @@ network.send([('blockchain.headers.subscribe',[])], callback)
# 3. wait for results # 3. wait for results
while network.is_connected(): while network.is_connected():
time.sleep(1) time.sleep(1)

9
scripts/get_history

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
from electrum import NetworkProxy, print_json from electrum import Network, print_json
try: try:
addr = sys.argv[1] addr = sys.argv[1]
@ -9,8 +9,7 @@ except Exception:
print "usage: get_history <bitcoin_address>" print "usage: get_history <bitcoin_address>"
sys.exit(1) sys.exit(1)
n = NetworkProxy() n = Network()
n.start(start_daemon=True) n.start()
h = n.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0] h = n.synchronous_get(('blockchain.address.get_history',[addr]))
print_json(h) print_json(h)

20
scripts/merchant/merchant.py

@ -54,7 +54,7 @@ def check_create_table(conn):
c = conn.cursor() c = conn.cursor()
c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='electrum_payments';") c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='electrum_payments';")
data = c.fetchall() data = c.fetchall()
if not data: if not data:
c.execute("""CREATE TABLE electrum_payments (address VARCHAR(40), amount FLOAT, confirmations INT(8), received_at TIMESTAMP, expires_at TIMESTAMP, paid INT(1), processed INT(1));""") c.execute("""CREATE TABLE electrum_payments (address VARCHAR(40), amount FLOAT, confirmations INT(8), received_at TIMESTAMP, expires_at TIMESTAMP, paid INT(1), processed INT(1));""")
conn.commit() conn.commit()
@ -95,7 +95,7 @@ def on_wallet_update():
s = (value)/1.e8 s = (value)/1.e8
print "balance for %s:"%addr, s, requested_amount print "balance for %s:"%addr, s, requested_amount
if s>= requested_amount: if s>= requested_amount:
print "payment accepted", addr print "payment accepted", addr
out_queue.put( ('payment', addr)) out_queue.put( ('payment', addr))
@ -162,7 +162,7 @@ def send_command(cmd, params):
except socket.error: except socket.error:
print "Server not running" print "Server not running"
return 1 return 1
try: try:
out = f(*params) out = f(*params)
except socket.error: except socket.error:
@ -186,9 +186,9 @@ def db_thread():
data = cur.fetchall() data = cur.fetchall()
# add pending requests to the wallet # add pending requests to the wallet
for item in data: for item in data:
addr, amount, confirmations = item addr, amount, confirmations = item
if addr in pending_requests: if addr in pending_requests:
continue continue
else: else:
with wallet.lock: with wallet.lock:
@ -216,7 +216,7 @@ def db_thread():
print sql print sql
cur.execute(sql) cur.execute(sql)
# set paid=0 for expired requests # set paid=0 for expired requests
cur.execute("""UPDATE electrum_payments set paid=0 WHERE expires_at < CURRENT_TIMESTAMP and paid is NULL;""") cur.execute("""UPDATE electrum_payments set paid=0 WHERE expires_at < CURRENT_TIMESTAMP and paid is NULL;""")
# do callback for addresses that received payment or expired # do callback for addresses that received payment or expired
@ -241,7 +241,7 @@ def db_thread():
except ValueError, e: except ValueError, e:
print e print e
print "cannot do callback", data_json print "cannot do callback", data_json
conn.commit() conn.commit()
conn.close() conn.close()
@ -259,8 +259,7 @@ if __name__ == '__main__':
# start network # start network
c = electrum.SimpleConfig({'wallet_path':wallet_path}) c = electrum.SimpleConfig({'wallet_path':wallet_path})
daemon_socket = electrum.daemon.get_daemon(c, True) network = electrum.Network(config)
network = electrum.NetworkProxy(daemon_socket, config)
network.start() network.start()
# wait until connected # wait until connected
@ -284,7 +283,7 @@ if __name__ == '__main__':
network.register_callback('updated', on_wallet_update) network.register_callback('updated', on_wallet_update)
threading.Thread(target=db_thread, args=()).start() threading.Thread(target=db_thread, args=()).start()
out_queue = Queue.Queue() out_queue = Queue.Queue()
# server thread # server thread
from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
@ -299,4 +298,3 @@ if __name__ == '__main__':
server.handle_request() server.handle_request()
except socket.timeout: except socket.timeout:
continue continue

4
scripts/watch_address

@ -12,8 +12,7 @@ except Exception:
# start network # start network
c = electrum.SimpleConfig() c = electrum.SimpleConfig()
s = electrum.daemon.get_daemon(c,True) network = electrum.Network(c)
network = electrum.NetworkProxy(s,c)
network.start() network.start()
# wait until connected # wait until connected
@ -31,4 +30,3 @@ network.send([('blockchain.address.subscribe',[addr])], callback)
# 3. wait for results # 3. wait for results
while network.is_connected(): while network.is_connected():
time.sleep(1) time.sleep(1)

Loading…
Cancel
Save