electrum

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

commit 15632adb40aef519933dc210638b359990c458da
parent b2bfd5af1f7f44337e69655c9ef86fbd44a316fc
Author: Neil Booth <kyuupichan@gmail.com>
Date:   Sat,  4 Jul 2015 16:45:08 +0900

Rename send_tx() to sign_tx()

Because it signs, and it doesn't send.

Diffstat:
Mgui/qt/main_window.py | 6+++---
Mgui/qt/transaction_dialog.py | 2+-
Mplugins/btchipwallet.py | 89+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mplugins/trustedcoin.py | 42+++++++++++++++++++++---------------------
4 files changed, 69 insertions(+), 70 deletions(-)

diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -1137,18 +1137,18 @@ class ElectrumWindow(QMainWindow): self.do_clear() else: self.broadcast_transaction(tx, tx_desc) - self.send_tx(tx, sign_done) + self.sign_tx(tx, sign_done) @protected - def send_tx(self, tx, callback, password): + def sign_tx(self, tx, callback, password): '''Sign the transaction in a separate thread. When done, calls the callback with a success code of True or False. ''' self.send_button.setDisabled(True) # call hook to see if plugin needs gui interaction - run_hook('send_tx', tx) + run_hook('sign_tx', tx) # sign the tx success = [False] # Array to work around python scoping diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py @@ -145,7 +145,7 @@ class TxDialog(QWidget): self.prompt_if_unsaved = True self.saved = False self.update() - self.parent.send_tx(self.tx, sign_done) + self.parent.sign_tx(self.tx, sign_done) def save(self): name = 'signed_%s.txn' % (self.tx.hash()[0:8]) if self.tx.is_complete() else 'unsigned.txn' diff --git a/plugins/btchipwallet.py b/plugins/btchipwallet.py @@ -28,7 +28,7 @@ try: from btchip.btchipFirmwareWizard import checkFirmware, updateFirmware from btchip.btchipException import BTChipException BTCHIP = True - BTCHIP_DEBUG = False + BTCHIP_DEBUG = False except ImportError: BTCHIP = False @@ -45,7 +45,7 @@ class Plugin(BasePlugin): def _init(self): return BTCHIP - def is_available(self): + def is_available(self): if not self._is_available: return False if not self.wallet: @@ -82,7 +82,7 @@ class Plugin(BasePlugin): else: QMessageBox.information(self.window, _('Error'), _("BTChip device not detected.\nContinuing in watching-only mode."), _('OK')) self.wallet.force_watching_only = True - + @hook def installwizard_restore(self, wizard, storage): if storage.get('wallet_type') != 'btchip': @@ -96,7 +96,7 @@ class Plugin(BasePlugin): return wallet @hook - def send_tx(self, tx): + def sign_tx(self, tx): tx.error = None try: self.wallet.sign_transaction(tx, None) @@ -124,7 +124,7 @@ class BTChipWallet(BIP32_HD_Wallet): if clear_client and self.client is not None: self.client.bad = True self.device_checked = False - raise Exception(message) + raise Exception(message) def get_action(self): if not self.accounts: @@ -155,7 +155,7 @@ class BTChipWallet(BIP32_HD_Wallet): d.setWaitImpl(DongleWaitQT(d)) self.client = btchip(d) firmware = self.client.getFirmwareVersion()['version'].split(".") - if not checkFirmware(firmware): + if not checkFirmware(firmware): d.close() try: updateFirmware() @@ -164,13 +164,13 @@ class BTChipWallet(BIP32_HD_Wallet): raise e d = getDongle(BTCHIP_DEBUG) d.setWaitImpl(DongleWaitQT(d)) - self.client = btchip(d) + self.client = btchip(d) try: self.client.getOperationMode() except BTChipException, e: if (e.sw == 0x6985): d.close() - dialog = StartBTChipPersoDialog() + dialog = StartBTChipPersoDialog() dialog.exec_() # Then fetch the reference again as it was invalidated d = getDongle(BTCHIP_DEBUG) @@ -178,18 +178,18 @@ class BTChipWallet(BIP32_HD_Wallet): self.client = btchip(d) else: raise e - if not noPin: + if not noPin: # Immediately prompts for the PIN - remaining_attempts = self.client.getVerifyPinRemainingAttempts() + remaining_attempts = self.client.getVerifyPinRemainingAttempts() if remaining_attempts <> 1: msg = "Enter your BTChip PIN - remaining attempts : " + str(remaining_attempts) else: msg = "Enter your BTChip PIN - WARNING : LAST ATTEMPT. If the PIN is not correct, the dongle will be wiped." - confirmed, p, pin = self.password_dialog(msg) + confirmed, p, pin = self.password_dialog(msg) if not confirmed: aborted = True raise Exception('Aborted by user - please unplug the dongle and plug it again before retrying') - pin = pin.encode() + pin = pin.encode() self.client.verifyPin(pin) except BTChipException, e: @@ -197,18 +197,18 @@ class BTChipWallet(BIP32_HD_Wallet): self.client.dongle.close() except: pass - self.client = None + self.client = None if (e.sw == 0x6faa): - raise Exception("Dongle is temporarily locked - please unplug it and replug it again") + raise Exception("Dongle is temporarily locked - please unplug it and replug it again") if ((e.sw & 0xFFF0) == 0x63c0): raise Exception("Invalid PIN - please unplug the dongle and plug it again before retrying") raise e except Exception, e: - try: + try: self.client.dongle.close() except: - pass - self.client = None + pass + self.client = None if not aborted: raise Exception("Could not connect to your BTChip dongle. Please verify access permissions, PIN, or unplug the dongle and plug it again") else: @@ -234,20 +234,20 @@ class BTChipWallet(BIP32_HD_Wallet): return [] def get_public_key(self, bip32_path): - # S-L-O-W - we don't handle the fingerprint directly, so compute it manually from the previous node + # S-L-O-W - we don't handle the fingerprint directly, so compute it manually from the previous node # This only happens once so it's bearable - self.get_client() # prompt for the PIN before displaying the dialog if necessary + self.get_client() # prompt for the PIN before displaying the dialog if necessary waitDialog.start("Computing master public key") - try: + try: splitPath = bip32_path.split('/') - fingerprint = 0 + fingerprint = 0 if len(splitPath) > 1: prevPath = "/".join(splitPath[0:len(splitPath) - 1]) nodeData = self.get_client().getWalletPublicKey(prevPath) publicKey = compress_public_key(nodeData['publicKey']) h = hashlib.new('ripemd160') h.update(hashlib.sha256(publicKey).digest()) - fingerprint = unpack(">I", h.digest()[0:4])[0] + fingerprint = unpack(">I", h.digest()[0:4])[0] nodeData = self.get_client().getWalletPublicKey(bip32_path) publicKey = compress_public_key(nodeData['publicKey']) depth = len(splitPath) @@ -255,7 +255,7 @@ class BTChipWallet(BIP32_HD_Wallet): if len(lastChild) == 1: childnum = int(lastChild[0]) else: - childnum = 0x80000000 | int(lastChild[0]) + childnum = 0x80000000 | int(lastChild[0]) xpub = "0488B21E".decode('hex') + chr(depth) + self.i4b(fingerprint) + self.i4b(childnum) + str(nodeData['chainCode']) + str(publicKey) except Exception, e: self.give_error(e, True) @@ -270,7 +270,7 @@ class BTChipWallet(BIP32_HD_Wallet): self.mpk = self.get_public_key("44'/0'") return self.mpk except Exception, e: - self.give_error(e, True) + self.give_error(e, True) def i4b(self, x): return pack('>I', x) @@ -287,13 +287,13 @@ class BTChipWallet(BIP32_HD_Wallet): self.signing = True self.get_client() # prompt for the PIN before displaying the dialog if necessary if not self.check_proper_device(): - self.give_error('Wrong device or password') + self.give_error('Wrong device or password') address_path = self.address_id(address) waitDialog.start("Signing Message ...") try: info = self.get_client().signMessagePrepare(address_path, message) pin = "" - if info['confirmationNeeded']: + if info['confirmationNeeded']: # TODO : handle different confirmation types. For the time being only supports keyboard 2FA use2FA = True confirmed, p, pin = self.password_dialog() @@ -307,8 +307,8 @@ class BTChipWallet(BIP32_HD_Wallet): except BTChipException, e: if e.sw == 0x6a80: self.give_error("Unfortunately, this message cannot be signed by BTChip. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry.") - else: - self.give_error(e, True) + else: + self.give_error(e, True) except Exception, e: self.give_error(e, True) finally: @@ -332,22 +332,22 @@ class BTChipWallet(BIP32_HD_Wallet): # And convert it - return b64encode(chr(27 + 4 + (signature[0] & 0x01)) + r + s) + return b64encode(chr(27 + 4 + (signature[0] & 0x01)) + r + s) def sign_transaction(self, tx, password): if tx.is_complete(): return if tx.error: raise BaseException(tx.error) - self.signing = True + self.signing = True inputs = [] inputsPaths = [] pubKeys = [] trustedInputs = [] - redeemScripts = [] + redeemScripts = [] signatures = [] preparedTrustedInputs = [] - changePath = "" + changePath = "" changeAmount = None output = None outputAmount = None @@ -358,8 +358,8 @@ class BTChipWallet(BIP32_HD_Wallet): for txinput in tx.inputs: if ('is_coinbase' in txinput and txinput['is_coinbase']): self.give_error("Coinbase not supported") # should never happen - inputs.append([ self.transactions[txinput['prevout_hash']].raw, - txinput['prevout_n'] ]) + inputs.append([ self.transactions[txinput['prevout_hash']].raw, + txinput['prevout_n'] ]) address = txinput['address'] inputsPaths.append(self.address_id(address)) pubKeys.append(self.get_public_keys(address)) @@ -367,7 +367,7 @@ class BTChipWallet(BIP32_HD_Wallet): # Recognize outputs - only one output and one change is authorized if len(tx.outputs) > 2: # should never happen self.give_error("Transaction with more than 2 outputs not supported") - for type, address, amount in tx.outputs: + for type, address, amount in tx.outputs: assert type == 'address' if self.is_change(address): changePath = self.address_id(address) @@ -386,7 +386,7 @@ class BTChipWallet(BIP32_HD_Wallet): try: # Get trusted inputs from the original transactions for utxo in inputs: - txtmp = bitcoinTransaction(bytearray(utxo[0].decode('hex'))) + txtmp = bitcoinTransaction(bytearray(utxo[0].decode('hex'))) trustedInputs.append(self.get_client().getTrustedInput(txtmp, utxo[1])) # TODO : Support P2SH later redeemScripts.append(txtmp.outputs[utxo[1]].script) @@ -394,13 +394,13 @@ class BTChipWallet(BIP32_HD_Wallet): firstTransaction = True inputIndex = 0 while inputIndex < len(inputs): - self.get_client().startUntrustedTransaction(firstTransaction, inputIndex, + self.get_client().startUntrustedTransaction(firstTransaction, inputIndex, trustedInputs, redeemScripts[inputIndex]) - outputData = self.get_client().finalizeInput(output, format_satoshis_plain(outputAmount), + outputData = self.get_client().finalizeInput(output, format_satoshis_plain(outputAmount), format_satoshis_plain(self.get_tx_fee(tx)), changePath, bytearray(rawTx.decode('hex'))) if firstTransaction: transactionOutput = outputData['outputData'] - if outputData['confirmationNeeded']: + if outputData['confirmationNeeded']: use2FA = True # TODO : handle different confirmation types. For the time being only supports keyboard 2FA waitDialog.emit(SIGNAL('dongle_done')) @@ -410,16 +410,16 @@ class BTChipWallet(BIP32_HD_Wallet): msg = "Do not enter your device PIN here !\r\n\r\n" + \ "Your BTChip wants to talk to you and tell you a unique second factor code.\r\n" + \ "For this to work, please match the character between stars of the output address using your security card\r\n\r\n" + \ - "Output address : " + "Output address : " for index in range(len(output)): if index == outputData['keycardData'][keycardIndex]: msg = msg + "*" + output[index] + "*" else: msg = msg + output[index] - msg = msg + "\r\n" + msg = msg + "\r\n" confirmed, p, pin = self.password_dialog(msg) if not confirmed: - raise Exception('Aborted by user') + raise Exception('Aborted by user') try: pin2 = pin2 + chr(int(pin[0], 16)) except: @@ -428,7 +428,7 @@ class BTChipWallet(BIP32_HD_Wallet): else: confirmed, p, pin = self.password_dialog() if not confirmed: - raise Exception('Aborted by user') + raise Exception('Aborted by user') pin = pin.encode() self.client.bad = True self.device_checked = False @@ -452,7 +452,7 @@ class BTChipWallet(BIP32_HD_Wallet): inputIndex = 0 while inputIndex < len(inputs): # TODO : Support P2SH later - inputScript = get_regular_input_script(signatures[inputIndex], pubKeys[inputIndex][0].decode('hex')) + inputScript = get_regular_input_script(signatures[inputIndex], pubKeys[inputIndex][0].decode('hex')) preparedTrustedInputs.append([ trustedInputs[inputIndex]['value'], inputScript ]) inputIndex = inputIndex + 1 updatedTransaction = format_transaction(transactionOutput, preparedTrustedInputs) @@ -525,4 +525,3 @@ if BTCHIP: def waitFirstResponse(self, timeout): return self.dongle.waitFirstResponse(timeout) - diff --git a/plugins/trustedcoin.py b/plugins/trustedcoin.py @@ -62,7 +62,7 @@ class TrustedCoinCosignerClient(object): self.base_url = base_url self.debug = debug self.user_agent = user_agent - + def send_request(self, method, relative_url, data=None): kwargs = {'headers': {}} if self.user_agent: @@ -85,12 +85,12 @@ class TrustedCoinCosignerClient(object): r = response.json() if 'message' in r: message = r['message'] - raise TrustedCoinException(message, response.status_code) + raise TrustedCoinException(message, response.status_code) if response.headers.get('content-type') == 'application/json': return response.json() else: return response.text - + def get_terms_of_service(self, billing_plan='electrum-per-tx-otp'): """ Returns the TOS for the given billing plan as a plain/text unicode string. @@ -98,13 +98,13 @@ class TrustedCoinCosignerClient(object): """ payload = {'billing_plan': billing_plan} return self.send_request('get', 'tos', payload) - + def create(self, xpubkey1, xpubkey2, email, billing_plan='electrum-per-tx-otp'): """ Creates a new cosigner resource. :param xpubkey1: a bip32 extended public key (customarily the hot key) :param xpubkey2: a bip32 extended public key (customarily the cold key) - :param email: a contact email + :param email: a contact email :param billing_plan: the billing plan for the cosigner """ payload = { @@ -114,7 +114,7 @@ class TrustedCoinCosignerClient(object): 'billing_plan': billing_plan, } return self.send_request('post', 'cosigner', payload) - + def auth(self, id, otp): """ Attempt to authenticate for a particular cosigner. @@ -123,7 +123,7 @@ class TrustedCoinCosignerClient(object): """ payload = {'otp': otp} return self.send_request('post', 'cosigner/%s/auth' % quote(id), payload) - + def get(self, id): """ Attempt to authenticate for a particular cosigner. @@ -131,7 +131,7 @@ class TrustedCoinCosignerClient(object): :param otp: the one time password """ return self.send_request('get', 'cosigner/%s' % quote(id)) - + def sign(self, id, transaction, otp): """ Attempt to authenticate for a particular cosigner. @@ -157,7 +157,7 @@ class TrustedCoinCosignerClient(object): 'otp': otp, 'recipient': recipient, 'timestamp': int(time.time()), - + } relative_url = 'cosigner/%s/transfer' % quote(id) full_url = urljoin(self.base_url, relative_url) @@ -282,7 +282,7 @@ class Plugin(BasePlugin): wallet.add_cosigner_seed(' '.join(words[0:n]), 'x1/', password) wallet.add_cosigner_xpub(' '.join(words[n:]), 'x2/') - msg = [ + msg = [ _('Your wallet file is:') + " %s"%os.path.abspath(wallet.storage.path), _('You need to be online in order to complete the creation of your wallet.'), _('If you generated your seed on an offline computer, click on "%s" to close this window, move your wallet file to an online computer and reopen it with Electrum.') % _('Close'), @@ -350,14 +350,14 @@ class Plugin(BasePlugin): def get_wizard_action(self, window, wallet, action): if hasattr(self, action): return getattr(self, action) - + @hook def installwizard_restore(self, window, storage): - if storage.get('wallet_type') != '2fa': + if storage.get('wallet_type') != '2fa': return seed = window.enter_seed_dialog("Enter your seed", None, func=self.seed_func) - if not seed: + if not seed: return wallet = Wallet_2fa(storage) self.wallet = wallet @@ -380,7 +380,7 @@ class Plugin(BasePlugin): self.wallet = wallet self.window = window - if wallet.storage.get('wallet_type') != '2fa': + if wallet.storage.get('wallet_type') != '2fa': raise return @@ -444,8 +444,8 @@ class Plugin(BasePlugin): return False @hook - def send_tx(self, tx): - self.print_error("twofactor:send_tx") + def sign_tx(self, tx): + self.print_error("twofactor:sign_tx") if self.wallet.storage.get('wallet_type') != '2fa': return @@ -514,7 +514,7 @@ class Plugin(BasePlugin): self.print_error( "received answer", r) if not r: - return + return raw_tx = r.get('transaction') tx.update(raw_tx) @@ -534,7 +534,7 @@ class Plugin(BasePlugin): grid.addWidget(pw, 1, 1) vbox.addLayout(grid) vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) - if not d.exec_(): + if not d.exec_(): return return pw.get_amount() @@ -562,7 +562,7 @@ class Plugin(BasePlugin): + _("For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>" label = QLabel(msg) label.setOpenExternalLinks(1) - + hbox.addStretch(10) hbox.addWidget(logo) hbox.addStretch(10) @@ -656,7 +656,7 @@ class Plugin(BasePlugin): tos = server.get_terms_of_service() self.TOS = tos window.emit(SIGNAL('twofactor:TOS')) - + def on_result(): tos_e.setText(self.TOS) @@ -669,7 +669,7 @@ class Plugin(BasePlugin): email_e.textChanged.connect(lambda: accept_button.setEnabled(re.match(regexp,email_e.text()) is not None)) email_e.setFocus(True) - if not window.exec_(): + if not window.exec_(): return email = str(email_e.text())