diff --git a/gui/gui_classic/installwizard.py b/gui/gui_classic/installwizard.py index a83d06b50..a7454a279 100644 --- a/gui/gui_classic/installwizard.py +++ b/gui/gui_classic/installwizard.py @@ -242,17 +242,16 @@ class InstallWizard(QDialog): def restore_wallet(self, wallet): # wait until we are connected, because the user might have selected another server - if not wallet.interface.is_connected: - waiting = lambda: False if wallet.interface.is_connected else "%s \n" % (_("Connecting...")) + if not self.network.interface.is_connected: + waiting = lambda: False if self.network.interface.is_connected else "%s \n" % (_("Connecting...")) waiting_dialog(waiting) waiting = lambda: False if wallet.is_up_to_date() else "%s\n%s %d\n%s %.1f"\ - %(_("Please wait..."),_("Addresses generated:"),len(wallet.addresses(True)),_("Kilobytes received:"), wallet.interface.bytes_received/1024.) + %(_("Please wait..."),_("Addresses generated:"),len(wallet.addresses(True)),_("Kilobytes received:"), self.network.interface.bytes_received/1024.) # try to restore old account wallet.create_old_account() wallet.set_up_to_date(False) - wallet.interface.poke('synchronizer') waiting_dialog(waiting) if wallet.is_found(): @@ -262,7 +261,6 @@ class InstallWizard(QDialog): wallet.accounts.pop(0) wallet.create_accounts() wallet.set_up_to_date(False) - wallet.interface.poke('synchronizer') waiting_dialog(waiting) if wallet.is_found(): diff --git a/gui/gui_classic/lite_window.py b/gui/gui_classic/lite_window.py index b6cdd94ad..0b5c05406 100644 --- a/gui/gui_classic/lite_window.py +++ b/gui/gui_classic/lite_window.py @@ -832,6 +832,7 @@ class MiniDriver(QObject): super(QObject, self).__init__() self.wallet = wallet + self.network = wallet.network self.window = window self.wallet.network.register_callback('updated',self.update_callback) @@ -851,9 +852,9 @@ class MiniDriver(QObject): self.emit(SIGNAL("updatesignal()")) def update(self): - if not self.wallet.interface: + if not self.network.interface: self.initializing() - elif not self.wallet.interface.is_connected: + elif not self.network.interface.is_connected: self.connecting() elif not self.wallet.up_to_date: self.synchronizing() diff --git a/gui/gui_classic/main_window.py b/gui/gui_classic/main_window.py index 920d9c0aa..003cecc66 100644 --- a/gui/gui_classic/main_window.py +++ b/gui/gui_classic/main_window.py @@ -281,13 +281,11 @@ class ElectrumWindow(QMainWindow): self.show_message("file not found "+ filename) return - interface = self.wallet.interface - blockchain = self.wallet.verifier.blockchain self.wallet.stop_threads() # create new wallet wallet = Wallet(storage) - wallet.start_threads(interface, blockchain) + wallet.start_threads(network) self.load_wallet(wallet) @@ -302,7 +300,7 @@ class ElectrumWindow(QMainWindow): storage = WalletStorage({'wallet_path': filename}) assert not storage.file_exists - wizard = installwizard.InstallWizard(self.config, self.wallet.interface, self.wallet.verifier.blockchain, storage) + wizard = installwizard.InstallWizard(self.config, self.network, storage) wallet = wizard.run() if wallet: self.load_wallet(wallet) @@ -410,12 +408,12 @@ class ElectrumWindow(QMainWindow): def notify_transactions(self): print_error("Notifying GUI") - if len(self.wallet.interface.pending_transactions_for_notifications) > 0: + if len(self.network.interface.pending_transactions_for_notifications) > 0: # Combine the transactions if there are more then three - tx_amount = len(self.wallet.interface.pending_transactions_for_notifications) + tx_amount = len(self.network.interface.pending_transactions_for_notifications) if(tx_amount >= 3): total_amount = 0 - for tx in self.wallet.interface.pending_transactions_for_notifications: + for tx in self.network.interface.pending_transactions_for_notifications: is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx) if(v > 0): total_amount += v @@ -423,11 +421,11 @@ class ElectrumWindow(QMainWindow): self.notify("%s new transactions received. Total amount received in the new transactions %s %s" \ % (tx_amount, self.format_amount(total_amount), self.base_unit())) - self.wallet.interface.pending_transactions_for_notifications = [] + self.network.interface.pending_transactions_for_notifications = [] else: - for tx in self.wallet.interface.pending_transactions_for_notifications: + for tx in self.network.interface.pending_transactions_for_notifications: if tx: - self.wallet.interface.pending_transactions_for_notifications.remove(tx) + self.network.interface.pending_transactions_for_notifications.remove(tx) is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx) if(v > 0): self.notify("New transaction received. %s %s" % (self.format_amount(v), self.base_unit())) @@ -535,7 +533,7 @@ class ElectrumWindow(QMainWindow): return "BTC" if self.decimal_point == 8 else "mBTC" def update_status(self): - if self.wallet.interface and self.wallet.interface.is_connected: + if self.network.interface and self.network.interface.is_connected: if not self.wallet.up_to_date: text = _("Synchronizing...") icon = QIcon(":icons/status_waiting.png") @@ -555,7 +553,7 @@ class ElectrumWindow(QMainWindow): def update_wallet(self): self.update_status() - if self.wallet.up_to_date or not self.wallet.interface.is_connected: + if self.wallet.up_to_date or not self.network.interface.is_connected: self.update_history_tab() self.update_receive_tab() self.update_contacts_tab() @@ -1308,7 +1306,7 @@ class ElectrumWindow(QMainWindow): console.updateNamespace({'wallet' : self.wallet, 'network' : self.network, 'gui':self}) console.updateNamespace({'util' : util, 'bitcoin':bitcoin}) - c = commands.Commands(self.wallet, self.wallet.interface, lambda: self.console.set_json(True)) + c = commands.Commands(self.wallet, self.network.interface, lambda: self.console.set_json(True)) methods = {} def mkfunc(f, method): return lambda *args: apply( f, (method, args, self.password_dialog )) diff --git a/gui/gui_classic/network_dialog.py b/gui/gui_classic/network_dialog.py index 37c57d3d5..47bab286e 100644 --- a/gui/gui_classic/network_dialog.py +++ b/gui/gui_classic/network_dialog.py @@ -239,5 +239,6 @@ class NetworkDialog(QDialog): self.config.set_key("proxy", proxy, True) self.config.set_key("server", server, True) self.network.set_server(server, proxy) + self.config.set_key('auto_cycle', self.autocycle_cb.isChecked(), True) return True diff --git a/gui/gui_gtk.py b/gui/gui_gtk.py index c8b1a8115..635deb1c1 100644 --- a/gui/gui_gtk.py +++ b/gui/gui_gtk.py @@ -33,7 +33,7 @@ import platform MONOSPACE_FONT = 'Lucida Console' if platform.system() == 'Windows' else 'monospace' from electrum.util import format_satoshis -from electrum.interface import DEFAULT_SERVERS +from electrum.network import DEFAULT_SERVERS from electrum.bitcoin import MIN_RELAY_TX_FEE def numbify(entry, is_int = False): @@ -268,7 +268,7 @@ def run_settings_dialog(wallet, parent): def run_network_dialog( wallet, parent ): image = gtk.Image() image.set_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_DIALOG) - interface = wallet.interface + interface = wallet.network.interface if parent: if interface.is_connected: status = "Connected to %s:%d\n%d blocks"%(interface.host, interface.port, wallet.network.blockchain.height) @@ -279,7 +279,7 @@ def run_network_dialog( wallet, parent ): status = "Please choose a server.\nSelect cancel if you are offline." server = interface.server - servers = interface.get_servers() + servers = wallet.network.get_servers() dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, status) @@ -345,7 +345,7 @@ def run_network_dialog( wallet, parent ): treeview = gtk.TreeView(model=server_list) treeview.show() - if wallet.interface.servers: + if interface.servers: label = 'Active Servers' else: label = 'Default Servers' @@ -1107,7 +1107,7 @@ class ElectrumWindow: return vbox def update_status_bar(self): - interface = self.wallet.interface + interface = self.wallet.network.interface if self.funds_error: text = "Not enough funds" elif interface and interface.is_connected: @@ -1133,7 +1133,7 @@ class ElectrumWindow: self.update_history_tab() self.update_receiving_tab() # addressbook too... - self.info.set_text( self.wallet.interface.banner ) + self.info.set_text( self.wallet.network.banner ) self.wallet_updated = False def update_receiving_tab(self): diff --git a/gui/gui_text.py b/gui/gui_text.py index aa0bb5ae0..72ba085b2 100644 --- a/gui/gui_text.py +++ b/gui/gui_text.py @@ -102,7 +102,7 @@ class ElectrumGui: def print_balance(self): - if self.wallet.interface and self.wallet.interface.is_connected: + if self.network.interface and self.network.interface.is_connected: if not self.wallet.up_to_date: msg = _( "Synchronizing..." ) else: @@ -143,7 +143,7 @@ class ElectrumGui: self.stdscr.addstr( 12, 25, _("[Clear]"), curses.A_REVERSE if self.pos%6==5 else curses.color_pair(2)) def print_banner(self): - for i, x in enumerate( self.wallet.interface.banner.split('\n') ): + for i, x in enumerate( self.network.banner.split('\n') ): self.stdscr.addstr( 1+i, 1, x ) def print_list(self, list, firstline): @@ -318,7 +318,7 @@ class ElectrumGui: def network_dialog(self): out = self.run_dialog('Network', [ - {'label':'server', 'type':'str', 'value':self.wallet.interface.server}, + {'label':'server', 'type':'str', 'value':self.network.interface.server}, {'label':'proxy', 'type':'str', 'value':self.config.get('proxy', '')}, ], buttons = 1) if out: @@ -331,7 +331,7 @@ class ElectrumGui: self.wallet.config.set_key("proxy", proxy, True) self.wallet.config.set_key("server", server, True) - self.wallet.interface.set_server(server, proxy) + self.network.interface.set_server(server, proxy) diff --git a/lib/blockchain.py b/lib/blockchain.py index 94cc55957..d0563d6db 100644 --- a/lib/blockchain.py +++ b/lib/blockchain.py @@ -65,6 +65,8 @@ class Blockchain(threading.Thread): if not result: continue i, result = result + if not result: continue + header = result.get('result') height = header.get('block_height') self.servers_height[i.server] = height diff --git a/lib/interface.py b/lib/interface.py index 0dd3674b6..cf87442fa 100644 --- a/lib/interface.py +++ b/lib/interface.py @@ -381,6 +381,12 @@ class Interface(threading.Thread): raise BaseException('Unknown protocol: %s'%protocol) + def stop_subscriptions(self): + for callback in self.subscriptions.keys(): + callback(self, None) + self.subscriptions = {} + + def send(self, messages, callback): sub = [] @@ -430,23 +436,6 @@ class Interface(threading.Thread): return proxy - def set_server(self, server, proxy=None): - "todo: remove this" - # raise an error if the format isnt correct - a,b,c = server.split(':') - b = int(b) - assert c in 'stgh' - # set the server - if server != self.server or proxy != self.proxy: - print "changing server:", server, proxy - self.server = server - self.proxy = proxy - if self.is_connected and self.protocol in 'st' and self.s: - self.s.shutdown(socket.SHUT_RDWR) - self.s.close() - self.is_connected = False # this exits the polling loop - self.trigger_callback('disconnecting') # for actively disconnecting - def stop(self): if self.is_connected and self.protocol in 'st' and self.s: diff --git a/lib/network.py b/lib/network.py index c4d8cb001..cb3f7ea09 100644 --- a/lib/network.py +++ b/lib/network.py @@ -45,9 +45,9 @@ class Network(threading.Thread): self.interfaces = {} self.queue = Queue.Queue() self.default_server = self.config.get('server') - self.servers_list = filter_protocol(DEFAULT_SERVERS,'s') + self.disconnected_servers = [] self.callbacks = {} - #banner + self.servers = [] self.banner = '' @@ -66,13 +66,17 @@ class Network(threading.Thread): def random_server(self): - if len(self.servers_list) <= len(self.interfaces.keys()): - return + choice_list = [] + l = filter_protocol(self.get_servers(), 's') + for s in l: + if s in self.disconnected_servers or s in self.interfaces.keys(): + continue + else: + choice_list.append(s) - while True: - server = random.choice( self.servers_list ) - if server not in self.interfaces.keys(): break - + if not choice_list: return + + server = random.choice( choice_list ) return server @@ -116,11 +120,11 @@ class Network(threading.Thread): def set_server(self, server, proxy): - subscriptions = self.interface.subscriptions + i = self.interface self.default_server = server self.start_interface(server) self.interface = self.interfaces[server] - self.resend_subscriptions(subscriptions) + i.stop_subscriptions() # fixme: it should not stop all subscriptions, and send 'unsubscribe' self.trigger_callback('disconnecting') # for actively disconnecting @@ -138,8 +142,9 @@ class Network(threading.Thread): if i == self.interface: i.send([('server.banner',[])], self.on_banner) i.send([('server.peers.subscribe',[])], self.on_peers) + self.trigger_callback('connected') else: - self.servers_list.remove(i.server) + self.disconnected_servers.append(i.server) self.interfaces.pop(i.server) self.start_random_interface() @@ -154,6 +159,7 @@ class Network(threading.Thread): self.blockchain.queue.put((i,result)) def on_peers(self, i, r): + if not r: return self.servers = self.parse_servers(r.get('result')) self.trigger_callback('peers') @@ -200,12 +206,6 @@ class Network(threading.Thread): return servers - def resend_subscriptions(self, subscriptions): - for channel, messages in subscriptions.items(): - if messages: - self.interface.send(messages, channel) - - if __name__ == "__main__": diff --git a/lib/wallet.py b/lib/wallet.py index 88e632bf2..60d38f8ea 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -224,7 +224,6 @@ class Wallet: def update(self): self.up_to_date = False - #self.interface.poke('synchronizer') while not self.is_up_to_date(): time.sleep(0.1) @@ -1039,7 +1038,7 @@ class Wallet: print_error("received transaction that is no longer referenced in history", tx_hash) return self.transactions[tx_hash] = tx - self.interface.pending_transactions_for_notifications.append(tx) + self.network.interface.pending_transactions_for_notifications.append(tx) self.save_transactions() if self.verifier and tx_height>0: self.verifier.add(tx_hash, tx_height) @@ -1188,7 +1187,7 @@ class Wallet: def send_tx(self, tx): # asynchronous self.tx_event.clear() - self.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast) + self.network.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast) return tx.hash() def on_broadcast(self, i, result): @@ -1320,7 +1319,7 @@ class Wallet: # assert not self.is_mine(_addr) ext_requests.append( ('blockchain.address.get_history', [_addr]) ) - ext_h = self.interface.synchronous_get(ext_requests) + ext_h = self.network.interface.synchronous_get(ext_requests) print_error("sync:", ext_requests, ext_h) height = None for h in ext_h: @@ -1362,7 +1361,6 @@ class Wallet: def start_threads(self, network): from verifier import TxVerifier self.network = network - self.interface = network.interface self.verifier = TxVerifier(self.network, self.storage) self.verifier.start() self.set_verifier(self.verifier) @@ -1385,7 +1383,7 @@ class WalletSynchronizer(threading.Thread): self.daemon = True self.wallet = wallet wallet.synchronizer = self - self.interface = self.wallet.interface + self.network = self.wallet.network #self.wallet.network.register_callback('connected', lambda: self.wallet.set_up_to_date(False)) self.was_updated = True self.running = False @@ -1403,15 +1401,25 @@ class WalletSynchronizer(threading.Thread): messages = [] for addr in addresses: messages.append(('blockchain.address.subscribe', [addr])) - self.interface.send( messages, lambda i,r: self.queue.put(r)) + self.network.interface.send( messages, lambda i,r: self.queue.put(r)) def run(self): - if not self.interface.is_connected: - print_error( "synchronizer: waiting for interface") - self.interface.connect_event.wait() + with self.lock: + self.running = True + + while self.is_running(): + interface = self.network.interface + if not interface.is_connected: + print_error("synchronizer: waiting for interface") + interface.connect_event.wait() + + self.run_interface(interface) - with self.lock: self.running = True + + def run_interface(self, interface): + + print_error("synchronizer: connected to", interface.server) requested_tx = [] missing_tx = [] @@ -1423,12 +1431,10 @@ class WalletSynchronizer(threading.Thread): for tx_hash, tx_height in history: if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx: missing_tx.append( (tx_hash, tx_height) ) - print_error("missing tx", missing_tx) - # wait until we are connected, in case the user is not connected - while not self.interface.is_connected: - time.sleep(1) - + if missing_tx: + print_error("missing tx", missing_tx) + # subscriptions self.subscribe_to_addresses(self.wallet.addresses(True, next=True)) @@ -1443,12 +1449,12 @@ class WalletSynchronizer(threading.Thread): # request missing transactions for tx_hash, tx_height in missing_tx: if (tx_hash, tx_height) not in requested_tx: - self.interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r)) + interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r)) requested_tx.append( (tx_hash, tx_height) ) missing_tx = [] # detect if situation has changed - if self.interface.is_up_to_date() and self.queue.empty(): + if interface.is_up_to_date() and self.queue.empty(): if not self.wallet.is_up_to_date(): self.wallet.set_up_to_date(True) self.was_updated = True @@ -1462,10 +1468,16 @@ class WalletSynchronizer(threading.Thread): self.was_updated = False # 2. get a response - r = self.queue.get(block=True, timeout=10000000000) + try: + r = self.queue.get(block=True, timeout=1) + except Queue.Empty: + continue - # poke sends None. (needed during stop) - if not r: continue + if interface != self.network.interface: + break + + if not r: + continue # 3. handle response method = r['method'] @@ -1480,7 +1492,7 @@ class WalletSynchronizer(threading.Thread): addr = params[0] if self.wallet.get_status(self.wallet.get_history(addr)) != result: if requested_histories.get(addr) is None: - self.interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r)) + interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r)) requested_histories[addr] = result elif method == 'blockchain.address.get_history':