def __init__(self, storage): BIP32_HD_Wallet.__init__(self, storage) self.transport = None self.client = None self.mpk = None self.device_checked = False self.signing = False self.force_watching_only = False
def sign_transaction(self, tx, password): if self.has_seed(): return BIP32_HD_Wallet.sign_transaction(self, tx, password) if tx.is_complete(): return if not self.check_proper_device(): give_error('Wrong device or password') # previous transactions used as inputs prev_tx = {} # path of the xpubs that are involved xpub_path = {} for txin in tx.inputs: tx_hash = txin['prevout_hash'] ptx = self.transactions.get(tx_hash) if ptx is None: ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash])) ptx = Transaction(ptx) prev_tx[tx_hash] = ptx for x_pubkey in txin['x_pubkeys']: account_derivation = None if not is_extended_pubkey(x_pubkey): continue xpub = x_to_xpub(x_pubkey) for k, v in self.master_public_keys.items(): if v == xpub: account_id = re.match("x/(\d+)'", k).group(1) account_derivation = "44'/2'/%s'"%account_id if account_derivation is not None: xpub_path[xpub] = account_derivation self.plugin.sign_transaction(tx, prev_tx, xpub_path)
def sign_message(self, address, message, password): if self.has_seed(): return BIP32_HD_Wallet.sign_message(self, address, message, password) use2FA = False 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') address_path = self.address_id(address) self.plugin.handler.show_message("Signing message ...") try: info = self.get_client().signMessagePrepare(address_path, message) pin = "" if info['confirmationNeeded']: # TODO : handle different confirmation types. For the time being only supports keyboard 2FA use2FA = True confirmed, p, pin = self.password_dialog() if not confirmed: raise Exception('Aborted by user') pin = pin.encode() self.client.bad = True self.device_checked = False self.get_client(True) signature = self.get_client().signMessageSign(pin) except BTChipException, e: if e.sw == 0x6a80: self.give_error("Unfortunately, this message cannot be signed by the Ledger wallet. 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)
def sign_transaction(self, tx, password): if self.has_seed(): return BIP32_HD_Wallet.sign_transaction(self, tx, password) if tx.is_complete(): return if not self.check_proper_device(): give_error('Wrong device or password') # previous transactions used as inputs prev_tx = {} # path of the xpubs that are involved xpub_path = {} for txin in tx.inputs: tx_hash = txin['prevout_hash'] ptx = self.transactions.get(tx_hash) if ptx is None: ptx = self.network.synchronous_get( ('blockchain.transaction.get', [tx_hash])) ptx = Transaction(ptx) prev_tx[tx_hash] = ptx for x_pubkey in txin['x_pubkeys']: account_derivation = None if not is_extended_pubkey(x_pubkey): continue xpub = x_to_xpub(x_pubkey) for k, v in self.master_public_keys.items(): if v == xpub: account_id = re.match("x/(\d+)'", k).group(1) account_derivation = "44'/2'/%s'" % account_id if account_derivation is not None: xpub_path[xpub] = account_derivation self.plugin.sign_transaction(tx, prev_tx, xpub_path)
def sign_message(self, address, message, password): if self.has_seed(): return BIP32_HD_Wallet.sign_message(self, address, message, password) use2FA = False 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') address_path = self.address_id(address) self.plugin.handler.show_message("Signing message ...") try: info = self.get_client().signMessagePrepare(address_path, message) pin = "" if info['confirmationNeeded']: # TODO : handle different confirmation types. For the time being only supports keyboard 2FA use2FA = True confirmed, p, pin = self.password_dialog() if not confirmed: raise Exception('Aborted by user') pin = pin.encode() self.client.bad = True self.device_checked = False self.get_client(True) signature = self.get_client().signMessageSign(pin) except BTChipException, e: if e.sw == 0x6a80: self.give_error( "Unfortunately, this message cannot be signed by the Ledger wallet. 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)
def sign_message(self, address, message, password): if self.has_seed(): return BIP32_HD_Wallet.sign_message(self, address, message, password) if not self.check_proper_device(): give_error('Wrong device or password') try: address_path = self.address_id(address) address_n = self.plugin.get_client().expand_path(address_path) except Exception, e: give_error(e)
def __init__(self, storage): BIP32_HD_Wallet.__init__(self, storage) self.mpk = None self.device_checked = False self.proper_device = False self.force_watching_only = False
def sign_transaction(self, tx, password): if self.has_seed(): return BIP32_HD_Wallet.sign_transaction(self, tx, password) if tx.is_complete(): return #if tx.error: # raise BaseException(tx.error) self.signing = True inputs = [] inputsPaths = [] pubKeys = [] trustedInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None use2FA = False pin = "" rawTx = tx.serialize() # Fetch inputs of the transaction to sign 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'] ]) address = txinput['address'] inputsPaths.append(self.address_id(address)) pubKeys.append(self.get_public_keys(address)) # 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: assert type == 'address' if self.is_change(address): changePath = self.address_id(address) changeAmount = amount else: if output <> None: # should never happen self.give_error("Multiple outputs with no change not supported") output = address if not self.canAlternateCoinVersions: v, h = bc_address_to_hash_160(address) if v == 38: output = hash_160_to_bc_address(h, 0) outputAmount = amount 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.plugin.handler.show_message("Signing Transaction ...") try: # Get trusted inputs from the original transactions for utxo in inputs: 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) # Sign all inputs firstTransaction = True inputIndex = 0 while inputIndex < len(inputs): self.get_client().startUntrustedTransaction(firstTransaction, inputIndex, trustedInputs, redeemScripts[inputIndex]) 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']: # TODO : handle different confirmation types. For the time being only supports keyboard 2FA self.plugin.handler.stop() if 'keycardData' in outputData: pin2 = "" for keycardIndex in range(len(outputData['keycardData'])): msg = "Do not enter your device PIN here !\r\n\r\n" + \ "Your Ledger Wallet 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 : " for index in range(len(output)): if index == outputData['keycardData'][keycardIndex]: msg = msg + "*" + output[index] + "*" else: msg = msg + output[index] msg = msg + "\r\n" confirmed, p, pin = self.password_dialog(msg) if not confirmed: raise Exception('Aborted by user') try: pin2 = pin2 + chr(int(pin[0], 16)) except: raise Exception('Invalid PIN character') pin = pin2 else: use2FA = True confirmed, p, pin = self.password_dialog() if not confirmed: raise Exception('Aborted by user') pin = pin.encode() self.client.bad = True self.device_checked = False self.get_client(True) self.plugin.handler.show_message("Signing ...") else: # Sign input with the provided PIN inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 firstTransaction = False except Exception, e: self.give_error(e, True)
def sign_transaction(self, tx, password): if self.has_seed(): return BIP32_HD_Wallet.sign_transaction(self, tx, password) if tx.is_complete(): return #if tx.error: # raise BaseException(tx.error) self.signing = True inputs = [] inputsPaths = [] pubKeys = [] trustedInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None use2FA = False pin = "" rawTx = tx.serialize() # Fetch inputs of the transaction to sign 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'] ]) address = txinput['address'] inputsPaths.append(self.address_id(address)) pubKeys.append(self.get_public_keys(address)) # 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: assert type == 'address' if self.is_change(address): changePath = self.address_id(address) changeAmount = amount else: if output <> None: # should never happen self.give_error( "Multiple outputs with no change not supported") output = address if not self.canAlternateCoinVersions: v, h = bc_address_to_hash_160(address) if v == 38: output = hash_160_to_bc_address(h, 0) outputAmount = amount 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.plugin.handler.show_message("Signing Transaction ...") try: # Get trusted inputs from the original transactions for utxo in inputs: 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) # Sign all inputs firstTransaction = True inputIndex = 0 while inputIndex < len(inputs): self.get_client().startUntrustedTransaction( firstTransaction, inputIndex, trustedInputs, redeemScripts[inputIndex]) 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']: # TODO : handle different confirmation types. For the time being only supports keyboard 2FA self.plugin.handler.stop() if 'keycardData' in outputData: pin2 = "" for keycardIndex in range( len(outputData['keycardData'])): msg = "Do not enter your device PIN here !\r\n\r\n" + \ "Your Ledger Wallet 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 : " for index in range(len(output)): if index == outputData['keycardData'][ keycardIndex]: msg = msg + "*" + output[index] + "*" else: msg = msg + output[index] msg = msg + "\r\n" confirmed, p, pin = self.password_dialog(msg) if not confirmed: raise Exception('Aborted by user') try: pin2 = pin2 + chr(int(pin[0], 16)) except: raise Exception('Invalid PIN character') pin = pin2 else: use2FA = True confirmed, p, pin = self.password_dialog() if not confirmed: raise Exception('Aborted by user') pin = pin.encode() self.client.bad = True self.device_checked = False self.get_client(True) self.plugin.handler.show_message("Signing ...") else: # Sign input with the provided PIN inputSignature = self.get_client().untrustedHashSign( inputsPaths[inputIndex], pin) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 firstTransaction = False except Exception, e: self.give_error(e, True)