electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 175fdbcac6fc1bd5ed54ceb709200b746877935d
parent b727824eedd7324133e18f698cf7babc156fd801
Author: ThomasV <thomasv@electrum.org>
Date:   Sat, 21 Nov 2015 15:24:38 +0100

Disentangle plugins and window management; use Qt signals

Diffstat:
Mgui/qt/__init__.py | 5+++--
Mgui/qt/main_window.py | 3+--
Mlib/plugins.py | 23++---------------------
Mlib/wallet.py | 5-----
Mplugins/cosigner_pool.py | 47+++++++++++++++++++++--------------------------
Mplugins/email_requests.py | 6++----
Mplugins/exchange_rate.py | 98++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mplugins/labels.py | 2++
Mplugins/trustedcoin.py | 199++++++++++++++++++++++++++++++++++---------------------------------------------
9 files changed, 165 insertions(+), 223 deletions(-)

diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py @@ -91,6 +91,7 @@ class ElectrumGui: self.build_tray_menu() self.tray.show() self.app.connect(self.app, QtCore.SIGNAL('new_window'), self.start_new_window) + run_hook('init_qt', self) def build_tray_menu(self): # Avoid immediate GC of old menu when window closed via its action @@ -217,7 +218,7 @@ class ElectrumGui: w.show() self.windows.append(w) self.build_tray_menu() - self.plugins.on_new_window(w) + run_hook('on_new_window', w) if uri: w.pay_to_URI(uri) @@ -227,7 +228,7 @@ class ElectrumGui: def close_window(self, window): self.windows.remove(window) self.build_tray_menu() - self.plugins.on_close_window(window) + run_hook('on_close_window', window) def main(self): self.timer.start() diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -565,8 +565,6 @@ class ElectrumWindow(QMainWindow, PrintError): def update_wallet(self): self.update_status() - if self.wallet is None: - return if self.wallet.up_to_date or not self.network or not self.network.is_connected(): self.update_tabs() @@ -2868,6 +2866,7 @@ class ElectrumWindow(QMainWindow, PrintError): p = plugins.toggle_enabled(self.config, name) cb.setChecked(bool(p)) enable_settings_widget(p, name, i) + run_hook('init_qt', self.gui_object) for i, descr in enumerate(plugins.descriptions): name = descr['name'] diff --git a/lib/plugins.py b/lib/plugins.py @@ -39,7 +39,6 @@ class Plugins(PrintError): self.pathname = None self.plugins = {} - self.windows = [] self.network = None self.descriptions = plugins.descriptions for item in self.descriptions: @@ -52,6 +51,7 @@ class Plugins(PrintError): if config.get('use_' + name): self.load_plugin(config, name) + def get(self, name): return self.plugins.get(name) @@ -67,9 +67,6 @@ class Plugins(PrintError): else: p = __import__(full_name, fromlist=['electrum_plugins']) plugin = p.Plugin(self, config, name) - # Inform the plugin of our windows - for window in self.windows: - plugin.on_new_window(window) if self.network: self.network.add_jobs(plugin.thread_jobs()) self.plugins[name] = plugin @@ -80,6 +77,7 @@ class Plugins(PrintError): traceback.print_exc(file=sys.stdout) return None + def close_plugin(self, plugin): if self.network: self.network.remove_jobs(plugin.thread_jobs()) @@ -132,17 +130,6 @@ class Plugins(PrintError): if network: network.add_jobs(jobs) - def trigger(self, event, *args, **kwargs): - for plugin in self.plugins.values(): - getattr(plugin, event)(*args, **kwargs) - - def on_new_window(self, window): - self.windows.append(window) - self.trigger('on_new_window', window) - - def on_close_window(self, window): - self.windows.remove(window) - self.trigger('on_close_window', window) hook_names = set() @@ -226,9 +213,3 @@ class BasePlugin(PrintError): def settings_dialog(self): pass - # Events - def on_close_window(self, window): - pass - - def on_new_window(self, window): - pass diff --git a/lib/wallet.py b/lib/wallet.py @@ -1046,11 +1046,6 @@ class Abstract_Wallet(PrintError): # Sign if keypairs: tx.sign(keypairs) - # Run hook, and raise if error - tx.error = None - run_hook('sign_transaction', self, tx, password) - if tx.error: - raise BaseException(tx.error) def sendtx(self, tx): diff --git a/plugins/cosigner_pool.py b/plugins/cosigner_pool.py @@ -89,43 +89,38 @@ class Plugin(BasePlugin): self.keys = [] self.cosigner_list = [] + @hook def on_new_window(self, window): - self.update() + self.update(window) + @hook def on_close_window(self, window): - self.update() - - def available_wallets(self): - result = {} - for window in self.parent.windows: - if window.wallet.wallet_type in ['2of2', '2of3']: - result[window.wallet] = window - return result + self.update(window) def is_available(self): - return bool(self.available_wallets()) - - def update(self): - wallets = self.available_wallets() - if wallets: - if self.listener is None: - self.print_error("starting listener") - self.listener = Listener(self) - self.listener.start() + return True + + def update(self, window): + wallet = window.wallet + if wallet.wallet_type not in ['2of2', '2of3']: + return + if self.listener is None: + self.print_error("starting listener") + self.listener = Listener(self) + self.listener.start() elif self.listener: self.print_error("shutting down listener") self.listener.stop() self.listener = None self.keys = [] self.cosigner_list = [] - for wallet, window in wallets.items(): - for key, xpub in wallet.master_public_keys.items(): - K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex') - _hash = bitcoin.Hash(K).encode('hex') - if wallet.master_private_keys.get(key): - self.keys.append((key, _hash, window)) - else: - self.cosigner_list.append((window, xpub, K, _hash)) + for key, xpub in wallet.master_public_keys.items(): + K = bitcoin.deserialize_xkey(xpub)[-1].encode('hex') + _hash = bitcoin.Hash(K).encode('hex') + if wallet.master_private_keys.get(key): + self.keys.append((key, _hash, window)) + else: + self.cosigner_list.append((window, xpub, K, _hash)) if self.listener: self.listener.set_keyhashes([t[1] for t in self.keys]) diff --git a/plugins/email_requests.py b/plugins/email_requests.py @@ -129,10 +129,8 @@ class Plugin(BasePlugin): self.obj.emit(SIGNAL('email:new_invoice')) def new_invoice(self): - if self.parent.windows: - window = self.parent.windows[0] - window.invoices.add(self.pr) - window.update_invoices_list() + self.parent.invoices.add(self.pr) + #window.update_invoices_list() @hook def receive_list_menu(self, menu, addr): diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py @@ -30,10 +30,12 @@ CCY_PRECISIONS = {'BHD': 3, 'BIF': 0, 'BYR': 0, 'CLF': 4, 'CLP': 0, 'XPF': 0} class ExchangeBase(PrintError): - def __init__(self, sig): + + def __init__(self, on_quotes, on_history): self.history = {} self.quotes = {} - self.sig = sig + self.on_quotes = on_quotes + self.on_history = on_history def protocol(self): return "https" @@ -59,7 +61,7 @@ class ExchangeBase(PrintError): self.print_error("getting fx quotes for", ccy) self.quotes = self.get_rates(ccy) self.print_error("received fx quotes") - self.sig.emit(SIGNAL('fx_quotes')) + self.on_quotes() except Exception, e: self.print_error("failed fx quotes:", e) @@ -73,7 +75,7 @@ class ExchangeBase(PrintError): self.print_error("requesting fx history for", ccy) self.history[ccy] = self.historical_rates(ccy) self.print_error("received fx history for", ccy) - self.sig.emit(SIGNAL("fx_history")) + self.on_history() except Exception, e: self.print_error("failed fx history:", e) @@ -241,16 +243,11 @@ class Plugin(BasePlugin, ThreadJob): def __init__(self, parent, config, name): BasePlugin.__init__(self, parent, config, name) - # Signal object first - self.sig = QObject() - self.sig.connect(self.sig, SIGNAL('fx_quotes'), self.on_fx_quotes) - self.sig.connect(self.sig, SIGNAL('fx_history'), self.on_fx_history) self.ccy = self.config_ccy() self.history_used_spot = False self.ccy_combo = None self.hist_checkbox = None - self.windows = dict() - + self.app = None is_exchange = lambda obj: (inspect.isclass(obj) and issubclass(obj, ExchangeBase) and obj != ExchangeBase) @@ -268,7 +265,7 @@ class Plugin(BasePlugin, ThreadJob): def run(self): # This runs from the network thread which catches exceptions - if self.windows and self.timeout <= time.time(): + if self.timeout <= time.time(): self.timeout = time.time() + 150 self.exchange.update(self.ccy) @@ -285,24 +282,26 @@ class Plugin(BasePlugin, ThreadJob): def show_history(self): return self.config_history() and self.exchange.history_ccys() + def set_exchange(self, name): class_ = self.exchanges.get(name) or self.exchanges.values()[0] name = class_.__name__ self.print_error("using exchange", name) if self.config_exchange() != name: self.config.set_key('use_exchange', name, True) - self.exchange = class_(self.sig) + + on_quotes = lambda: self.app.emit(SIGNAL('new_fx_quotes')) + on_history = lambda: self.app.emit(SIGNAL('new_fx_history')) + self.exchange = class_(on_quotes, on_history) # A new exchange means new fx quotes, initially empty. Force # a quote refresh self.timeout = 0 self.get_historical_rates() - self.on_fx_quotes() + #self.on_fx_quotes() - def update_status_bars(self): - '''Update status bar fiat balance in all windows''' - for window in self.windows: - window.update_status() + + @hook def on_new_window(self, window): # Additional send and receive edit boxes send_e = AmountEdit(self.config_ccy) @@ -311,20 +310,23 @@ class Plugin(BasePlugin, ThreadJob): lambda: send_e.setFrozen(window.amount_e.isReadOnly())) receive_e = AmountEdit(self.config_ccy) window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft) - - self.windows[window] = {'edits': (send_e, receive_e), - 'last_edited': {}} + window.fiat_send_e = send_e + window.fiat_receive_e = receive_e self.connect_fields(window, window.amount_e, send_e, window.fee_e) self.connect_fields(window, window.receive_amount_e, receive_e, None) window.history_list.refresh_headers() window.update_status() + window.connect(window.app, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window)) + window.connect(window.app, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window)) + window.connect(window.app, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window)) + window.connect(window.app, SIGNAL('refresh_headers'), window.history_list.refresh_headers) + def connect_fields(self, window, btc_e, fiat_e, fee_e): - last_edited = self.windows[window]['last_edited'] def edit_changed(edit): edit.setStyleSheet(BLACK_FG) - last_edited[(fiat_e, btc_e)] = edit + fiat_e.is_last_edited = (edit == fiat_e) amount = edit.get_amount() rate = self.exchange_rate() if rate is None or amount is None: @@ -346,45 +348,43 @@ class Plugin(BasePlugin, ThreadJob): fiat_e.textEdited.connect(partial(edit_changed, fiat_e)) btc_e.textEdited.connect(partial(edit_changed, btc_e)) - last_edited[(fiat_e, btc_e)] = btc_e + fiat_e.is_last_edited = False @hook - def do_clear(self, window): - self.windows[window]['edits'][0].setText('') + def init_qt(self, gui): + self.app = gui.app - def on_close_window(self, window): - self.windows.pop(window) + @hook + def do_clear(self, window): + window.fiat_send_e.setText('') def close(self): # Get rid of hooks before updating status bars. BasePlugin.close(self) - self.update_status_bars() - self.refresh_headers() - for window, data in self.windows.items(): - for edit in data['edits']: - edit.hide() - window.update_status() - - def refresh_headers(self): - for window in self.windows: - window.history_list.refresh_headers() - - def on_fx_history(self): + self.app.emit(SIGNAL('close_fx_plugin')) + + def restore_window(self, window): + window.update_status() + window.history_list.refresh_headers() + window.fiat_send_e.hide() + window.fiat_receive_e.hide() + + def on_fx_history(self, window): '''Called when historical fx quotes are updated''' - for window in self.windows: - window.history_list.update() + window.history_list.update() - def on_fx_quotes(self): + def on_fx_quotes(self, window): '''Called when fresh spot fx quotes come in''' - self.update_status_bars() + window.update_status() self.populate_ccy_combo() # Refresh edits with the new rate - for window, data in self.windows.items(): - for edit in data['last_edited'].values(): - edit.textEdited.emit(edit.text()) + edit = window.fiat_send_e if window.fiat_send_e.is_last_edited else window.amount_e + edit.textEdited.emit(edit.text()) + edit = window.fiat_receive_e if window.fiat_receive_e.is_last_edited else window.receive_amount_e + edit.textEdited.emit(edit.text()) # History tab needs updating if it used spot if self.history_used_spot: - self.on_fx_history() + self.on_fx_history(window) def on_ccy_combo_change(self): '''Called when the chosen currency changes''' @@ -392,7 +392,7 @@ class Plugin(BasePlugin, ThreadJob): if ccy and ccy != self.ccy: self.ccy = ccy self.config.set_key('currency', ccy, True) - self.update_status_bars() + self.app.emit(SIGNAL('new_fx_quotes')) self.get_historical_rates() # Because self.ccy changes self.hist_checkbox_update() @@ -503,7 +503,7 @@ class Plugin(BasePlugin, ThreadJob): self.get_historical_rates() else: self.config.set_key('history_rates', 'unchecked') - self.refresh_headers() + self.app.emit(SIGNAL('refresh_headers')) def ok_clicked(): self.timeout = 0 diff --git a/plugins/labels.py b/plugins/labels.py @@ -35,6 +35,7 @@ class Plugin(BasePlugin): self.obj = QObject() self.obj.connect(self.obj, SIGNAL('labels:pulled'), self.on_pulled) + @hook def on_new_window(self, window): wallet = window.wallet nonce = self.get_nonce(wallet) @@ -53,6 +54,7 @@ class Plugin(BasePlugin): t.setDaemon(True) t.start() + @hook def on_close_window(self, window): self.wallets.pop(window.wallet) diff --git a/plugins/trustedcoin.py b/plugins/trustedcoin.py @@ -177,6 +177,8 @@ class Wallet_2fa(Multisig_Wallet): self.wallet_type = '2fa' self.m = 2 self.n = 3 + self.is_billing = False + self.billing_info = None def get_action(self): xpub1 = self.master_public_keys.get("x1/") @@ -194,29 +196,68 @@ class Wallet_2fa(Multisig_Wallet): def make_seed(self): return Mnemonic('english').make_seed(num_bits=256, prefix=SEED_PREFIX) + def can_sign_without_server(self): + return self.master_private_keys.get('x2/') is not None + + def extra_fee(self, tx): + if self.can_sign_without_server(): + return 0 + if self.billing_info.get('tx_remaining'): + return 0 + if self.is_billing: + return 0 + # trustedcoin won't charge if the total inputs is lower than their fee + price = int(self.price_per_tx.get(1)) + assert price <= 100000 + if tx.input_value() < price: + self.print_error("not charging for this tx") + return 0 + return price + def estimated_fee(self, tx, fee_per_kb): fee = Multisig_Wallet.estimated_fee(self, tx, fee_per_kb) - x = run_hook('extra_fee', self, tx) - if x: fee += x + fee += self.extra_fee(tx) return fee def get_tx_fee(self, tx): fee = Multisig_Wallet.get_tx_fee(self, tx) - x = run_hook('extra_fee', self, tx) - if x: fee += x + fee += self.extra_fee(tx) return fee -# Utility functions + def make_unsigned_transaction(self, *args): + tx = BIP32_Wallet.make_unsigned_transaction(self, *args) + fee = self.extra_fee(tx) + if fee: + address = self.billing_info['billing_address'] + tx.outputs.append(('address', address, fee)) + return tx + + def sign_transaction(self, tx, password): + BIP32_Wallet.sign_transaction(self, tx, password) + if tx.is_complete(): + return + if not self.auth_code: + self.print_error("sign_transaction: no auth code") + return + long_user_id, short_id = self.get_user_id() + tx_dict = tx.as_dict() + raw_tx = tx_dict["hex"] + r = server.sign(short_id, raw_tx, self.auth_code) + if r: + raw_tx = r.get('transaction') + tx.update(raw_tx) + self.print_error("twofactor: is complete", tx.is_complete()) -def get_user_id(wallet): - def make_long_id(xpub_hot, xpub_cold): - return bitcoin.sha256(''.join(sorted([xpub_hot, xpub_cold]))) + def get_user_id(self): + def make_long_id(xpub_hot, xpub_cold): + return bitcoin.sha256(''.join(sorted([xpub_hot, xpub_cold]))) + xpub_hot = self.master_public_keys["x1/"] + xpub_cold = self.master_public_keys["x2/"] + long_id = make_long_id(xpub_hot, xpub_cold) + short_id = hashlib.sha256(long_id).hexdigest() + return long_id, short_id - xpub_hot = wallet.master_public_keys["x1/"] - xpub_cold = wallet.master_public_keys["x2/"] - long_id = make_long_id(xpub_hot, xpub_cold) - short_id = hashlib.sha256(long_id).hexdigest() - return long_id, short_id +# Utility functions def make_xpub(xpub, s): _, _, _, c, cK = deserialize_xkey(xpub) @@ -225,12 +266,12 @@ def make_xpub(xpub, s): return EncodeBase58Check(xpub2) def restore_third_key(wallet): - long_user_id, short_id = get_user_id(wallet) + long_user_id, short_id = wallet.get_user_id() xpub3 = make_xpub(signing_xpub, long_user_id) wallet.add_master_public_key('x3/', xpub3) def make_billing_address(wallet, num): - long_id, short_id = get_user_id(wallet) + long_id, short_id = wallet.get_user_id() xpub = make_xpub(billing_xpub, long_id) _, _, _, c, cK = deserialize_xkey(xpub) cK, c = bitcoin.CKD_pub(cK, c, num) @@ -240,7 +281,7 @@ def make_billing_address(wallet, num): def need_server(wallet, tx): from electrum.account import BIP32_Account # Detect if the server is needed - long_id, short_id = get_user_id(wallet) + long_id, short_id = wallet.get_user_id() xpub3 = wallet.master_public_keys['x3/'] for x in tx.inputs_to_sign(): if x[0:2] == 'ff': @@ -254,25 +295,20 @@ class Plugin(BasePlugin): def __init__(self, parent, config, name): BasePlugin.__init__(self, parent, config, name) self.seed_func = lambda x: bitcoin.is_new_seed(x, SEED_PREFIX) - # Keyed by wallet to handle multiple pertinent windows. Each - # wallet is a 2fa wallet. Each value is a dictionary with - # information about the wallet for the plugin - self.wallets = {} def constructor(self, s): return Wallet_2fa(s) def is_available(self): - return bool(self.wallets) + return True def set_enabled(self, wallet, enabled): wallet.storage.put('use_' + self.name, enabled) def is_enabled(self): - return (self.is_available() and - not all(wallet.master_private_keys.get('x2/') - for wallet in self.wallets.keys())) + return True + @hook def on_new_window(self, window): wallet = window.wallet if wallet.storage.get('wallet_type') == '2fa': @@ -280,25 +316,16 @@ class Plugin(BasePlugin): _("TrustedCoin"), partial(self.settings_dialog, window)) window.statusBar().addPermanentWidget(button) - self.wallets[wallet] = { - 'is_billing' : False, - 'billing_info' : None, - 'button' : button, # Avoid loss to GC - } t = Thread(target=self.request_billing_info, args=(wallet,)) t.setDaemon(True) t.start() - def on_close_window(self, window): - self.wallets.pop(window.wallet, None) - def request_billing_info(self, wallet): - billing_info = server.get(get_user_id(wallet)[1]) + billing_info = server.get(wallet.get_user_id()[1]) billing_address = make_billing_address(wallet, billing_info['billing_index']) assert billing_address == billing_info['billing_address'] - wallet_info = self.wallets[wallet] - wallet_info['billing_info'] = billing_info - wallet_info['price_per_tx'] = dict(billing_info['price_per_tx']) + wallet.billing_info = billing_info + wallet.price_per_tx = dict(billing_info['price_per_tx']) return True def create_extended_seed(self, wallet, window): @@ -352,7 +379,7 @@ class Plugin(BasePlugin): @hook def do_clear(self, window): - self.wallets[window.wallet]['is_billing'] = False + window.wallet.is_billing = False @hook def get_wizard_action(self, window, wallet, action): @@ -396,7 +423,7 @@ class Plugin(BasePlugin): xpub_cold = wallet.master_public_keys["x2/"] # Generate third key deterministically. - long_user_id, short_id = get_user_id(wallet) + long_user_id, short_id = wallet.get_user_id() xpub3 = make_xpub(signing_xpub, long_user_id) # secret must be sent by the server @@ -453,86 +480,30 @@ class Plugin(BasePlugin): @hook def sign_tx(self, window, tx): self.print_error("twofactor:sign_tx") - if window.wallet in self.wallets: + wallet = window.wallet + if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server(): auth_code = None - if need_server(window.wallet, tx): + if need_server(wallet, tx): auth_code = self.auth_dialog(window) else: self.print_error("twofactor: xpub3 not needed") - self.wallets[window.wallet]['auth_code'] = auth_code + window.wallet.auth_code = auth_code @hook def abort_send(self, window): - if window.wallet in self.wallets: - wallet_info = self.wallets[window.wallet] - # request billing info before forming the transaction - wallet_info['billing_info'] = None - task = partial(self.request_billing_info, window.wallet) - waiting_dialog = WaitingDialog(window, 'please wait...', task) - waiting_dialog.start() - waiting_dialog.wait() - if wallet_info['billing_info'] is None: - window.show_message('Could not contact server') - return True + wallet = window.wallet + if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server(): + if wallet.billing_info is None: + # request billing info before forming the transaction + task = partial(self.request_billing_info, wallet) + waiting_dialog = WaitingDialog(window, 'please wait...', task) + waiting_dialog.start() + waiting_dialog.wait() + if wallet.billing_info is None: + window.show_message('Could not contact server') + return True return False - @hook - def extra_fee(self, wallet, tx): - if not wallet in self.wallets: - return 0 - wallet_info = self.wallets[wallet] - if wallet_info['billing_info'].get('tx_remaining'): - return 0 - if wallet_info['is_billing']: - return 0 - # trustedcoin won't charge if the total inputs is lower than their fee - price = int(wallet_info['price_per_tx'].get(1)) - assert price <= 100000 - if tx.input_value() < price: - self.print_error("not charging for this tx") - return 0 - return price - - @hook - def make_unsigned_transaction(self, wallet, tx): - if wallet in self.wallets: - price = self.extra_fee(wallet, tx) - if not price: - return - address = self.wallets[wallet]['billing_info']['billing_address'] - tx.outputs.append(('address', address, price)) - - @hook - def sign_transaction(self, wallet, tx, password): - self.print_error("twofactor:sign") - if not wallet in self.wallets: - self.print_error("not 2fa wallet") - return - - auth_code = self.wallets[wallet]['auth_code'] - if not auth_code: - self.print_error("sign_transaction: no auth code") - return - - if tx.is_complete(): - return - - long_user_id, short_id = get_user_id(wallet) - tx_dict = tx.as_dict() - raw_tx = tx_dict["hex"] - try: - r = server.sign(short_id, raw_tx, auth_code) - except Exception as e: - tx.error = str(e) - return - - self.print_error( "received answer", r) - if not r: - return - - raw_tx = r.get('transaction') - tx.update(raw_tx) - self.print_error("twofactor: is complete", tx.is_complete()) def settings_dialog(self, window): task = partial(self.request_billing_info, window.wallet) @@ -576,7 +547,7 @@ class Plugin(BasePlugin): grid = QGridLayout() vbox.addLayout(grid) - price_per_tx = self.wallets[wallet]['price_per_tx'] + price_per_tx = wallet.price_per_tx v = price_per_tx.get(1) grid.addWidget(QLabel(_("Price per transaction (not prepaid):")), 0, 0) grid.addWidget(QLabel(window.format_amount(v) + ' ' + window.base_unit()), 0, 1) @@ -596,7 +567,7 @@ class Plugin(BasePlugin): grid.addWidget(b, i, 2) i += 1 - n = self.wallets[wallet]['billing_info'].get('tx_remaining', 0) + n = wallet.billing_info.get('tx_remaining', 0) grid.addWidget(QLabel(_("Your wallet has %d prepaid transactions.")%n), i, 0) # tranfer button @@ -616,9 +587,9 @@ class Plugin(BasePlugin): d.close() if window.pluginsdialog: window.pluginsdialog.close() - wallet_info = self.wallets[window.wallet] - uri = "bitcoin:" + wallet_info['billing_info']['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000) - wallet_info['is_billing'] = True + wallet = window.wallet + uri = "bitcoin:" + wallet.billing_info['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000) + wallet.is_billing = True window.pay_to_URI(uri) window.payto_e.setFrozen(True) window.message_e.setFrozen(True)