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
Example #2
0
 def __init__(self, storage):
     BIP32_HD_Wallet.__init__(self, storage)
     self.transport = None
     self.client = None
     self.mpk = None
     self.device_checked = False
     self.force_watching_only = False
     always_hook('add_plugin', self)
Example #3
0
 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
Example #4
0
 def __init__(self, storage):
     BIP32_HD_Wallet.__init__(self, storage)
     self.transport = None
     self.client = None
     self.mpk = None
     self.device_checked = False
     self.force_watching_only = False
     always_hook('add_plugin', self)
Example #5
0
    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)
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
 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
Example #9
0
    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 == 48:
                        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)