Beispiel #1
0
 def get_xpub(self, bip32_path, xtype):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     if xtype in ['p2wpkh', 'p2wsh'] and not self.supports_native_segwit():
         raise Exception(MSG_NEEDS_FW_UPDATE_SEGWIT)
     if xtype in ['p2wpkh-p2sh', 'p2wsh-p2sh'] and not self.supports_segwit():
         raise Exception(MSG_NEEDS_FW_UPDATE_SEGWIT)
     splitPath = bip32_path.split('/')
     if splitPath[0] == 'm':
         splitPath = splitPath[1:]
         bip32_path = bip32_path[2:]
     fingerprint = 0
     if len(splitPath) > 1:
         prevPath = "/".join(splitPath[0:len(splitPath) - 1])
         nodeData = self.dongleObject.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]
     nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
     publicKey = compress_public_key(nodeData['publicKey'])
     depth = len(splitPath)
     lastChild = splitPath[len(splitPath) - 1].split('\'')
     childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0])
     xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'], publicKey, depth, self.i4b(fingerprint), self.i4b(childnum))
     return xpub
Beispiel #2
0
 def get_xpub(self, bip32_path):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     try:
         splitPath = bip32_path.split('/')
         if splitPath[0] == 'm':
             splitPath = splitPath[1:]
             bip32_path = bip32_path[2:]
         fingerprint = 0
         if len(splitPath) > 1:
             prevPath = "/".join(splitPath[0:len(splitPath) - 1])
             nodeData = self.dongleObject.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]
         nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         if len(lastChild) == 1:
             childnum = int(lastChild[0])
         else:
             childnum = 0x80000000 | int(lastChild[0])
         xpub = XPUB_HEADER.decode('hex') + chr(depth) + self.i4b(fingerprint) + self.i4b(childnum) + str(nodeData['chainCode']) + str(publicKey)
     except Exception, e:
         #self.give_error(e, True)
         return None
Beispiel #3
0
 def get_xpub(self, bip32_path):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     try:
         splitPath = bip32_path.split('/')
         if splitPath[0] == 'm':
             splitPath = splitPath[1:]
             bip32_path = bip32_path[2:]
         fingerprint = 0
         if len(splitPath) > 1:
             prevPath = "/".join(splitPath[0:len(splitPath) - 1])
             nodeData = self.dongleObject.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]
         nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         if len(lastChild) == 1:
             childnum = int(lastChild[0])
         else:
             childnum = 0x80000000 | int(lastChild[0])
         xpub = bitcoin.serialize_xpub(0, str(nodeData['chainCode']), str(publicKey), depth, self.i4b(fingerprint), self.i4b(childnum))
         return xpub
     except Exception as e:
         print_error(e)
         return None
 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
     # This only happens once so it's bearable
     self.get_client(
     )  # prompt for the PIN before displaying the dialog if necessary
     waitDialog.start("Computing master public key")
     try:
         splitPath = bip32_path.split('/')
         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]
         nodeData = self.get_client().getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         if len(lastChild) == 1:
             childnum = int(lastChild[0])
         else:
             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)
Beispiel #5
0
 def get_xpub(self, bip32_path, xtype):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     splitPath = bip32_path.split('/')
     if splitPath[0] == 'm':
         splitPath = splitPath[1:]
         bip32_path = bip32_path[2:]
     fingerprint = 0
     if len(splitPath) > 1:
         prevPath = "/".join(splitPath[0:len(splitPath) - 1])
         nodeData = self.dongleObject.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]
     nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
     publicKey = compress_public_key(nodeData['publicKey'])
     depth = len(splitPath)
     lastChild = splitPath[len(splitPath) - 1].split('\'')
     childnum = int(lastChild[0]) if len(
         lastChild) == 1 else 0x80000000 | int(lastChild[0])
     xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'], publicKey,
                                   depth, self.i4b(fingerprint),
                                   self.i4b(childnum))
     return xpub
Beispiel #6
0
 def get_xpub(self, bip32_path, xtype):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     if xtype in ['p2wpkh', 'p2wsh'] and not self.supports_native_segwit():
         raise UserFacingException(MSG_NEEDS_FW_UPDATE_SEGWIT)
     if xtype in ['p2wpkh-p2sh', 'p2wsh-p2sh'] and not self.supports_segwit():
         raise UserFacingException(MSG_NEEDS_FW_UPDATE_SEGWIT)
     splitPath = bip32_path.split('/')
     if splitPath[0] == 'm':
         splitPath = splitPath[1:]
         bip32_path = bip32_path[2:]
     fingerprint = 0
     if len(splitPath) > 1:
         prevPath = "/".join(splitPath[0:len(splitPath) - 1])
         nodeData = self.dongleObject.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]
     nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
     publicKey = compress_public_key(nodeData['publicKey'])
     depth = len(splitPath)
     lastChild = splitPath[len(splitPath) - 1].split('\'')
     childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0])
     return BIP32Node(xtype=xtype,
                      eckey=ecc.ECPubkey(publicKey),
                      chaincode=nodeData['chainCode'],
                      depth=depth,
                      fingerprint=self.i4b(fingerprint),
                      child_number=self.i4b(childnum)).to_xpub()
Beispiel #7
0
    def get_master_public_key(self, bip32_path):
        self.checkDevice()
        # bip32_path is of the form 44'/0'/1'
        # 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.handler.show_message("Computing master public key")
        splitPath = bip32_path.split('/')
        if splitPath[0] == 'm':
            splitPath = splitPath[1:]
            bip32_path = bip32_path[2:]
        fingerprint = 0
        if len(splitPath) > 1:
            prevPath = "/".join(splitPath[0:len(splitPath) - 1])
            nodeData = self.dongleObject.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]
        nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
        publicKey = bytes(compress_public_key(nodeData['publicKey']))
        depth = len(splitPath)
        lastChild = splitPath[len(splitPath) - 1].split('\'')
        childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0])

        derivation = BIP32Derivation(chain_code=nodeData['chainCode'], depth=depth,
                                     parent_fingerprint=pack_be_uint32(fingerprint),
                                     n=childnum)
        return BIP32PublicKey(PublicKey.from_bytes(publicKey), derivation, Net.COIN)
Beispiel #8
0
 def get_xpub(self, bip32_path, xtype):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     if xtype in ['p2wpkh', 'p2wsh'] and not self.supports_native_segwit():
         raise UserFacingException(MSG_NEEDS_FW_UPDATE_SEGWIT)
     if xtype in ['p2wpkh-p2sh', 'p2wsh-p2sh'] and not self.supports_segwit():
         raise UserFacingException(MSG_NEEDS_FW_UPDATE_SEGWIT)
     bip32_path = bip32.normalize_bip32_derivation(bip32_path)
     bip32_intpath = bip32.convert_bip32_path_to_list_of_uint32(bip32_path)
     bip32_path = bip32_path[2:]  # cut off "m/"
     if len(bip32_intpath) >= 1:
         prevPath = bip32.convert_bip32_intpath_to_strpath(bip32_intpath[:-1])[2:]
         nodeData = self.dongleObject.getWalletPublicKey(prevPath)
         publicKey = compress_public_key(nodeData['publicKey'])
         fingerprint_bytes = hash_160(publicKey)[0:4]
         childnum_bytes = bip32_intpath[-1].to_bytes(length=4, byteorder="big")
     else:
         fingerprint_bytes = bytes(4)
         childnum_bytes = bytes(4)
     nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
     publicKey = compress_public_key(nodeData['publicKey'])
     depth = len(bip32_intpath)
     return BIP32Node(xtype=xtype,
                      eckey=ecc.ECPubkey(bytes(publicKey)),
                      chaincode=nodeData['chainCode'],
                      depth=depth,
                      fingerprint=fingerprint_bytes,
                      child_number=childnum_bytes).to_xpub()
Beispiel #9
0
 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        
     # This only happens once so it's bearable
     self.get_client() # prompt for the PIN before displaying the dialog if necessary        
     waitDialog.start("Computing master public key")
     try:            
         splitPath = bip32_path.split('/')
         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]            
         nodeData = self.get_client().getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         if len(lastChild) == 1:
             childnum = int(lastChild[0])
         else:
             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)
Beispiel #10
0
    def get_xpub(self, bip32_path, xtype):
        self.checkDevice()
        # bip32_path is of the form 44'/0'/1'
        # 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.handler.show_message("Computing master public key")
        if xtype in ['p2wpkh', 'p2wsh'] and not self.supports_native_segwit():
            raise Exception(
                "Firmware version too old for Segwit support. Please update at https://www.ledgerwallet.com"
            )
        if xtype in ['p2wpkh-p2sh', 'p2wsh-p2sh'
                     ] and not self.supports_segwit():
            raise Exception(
                "Firmware version too old for Segwit support. Please update at https://www.ledgerwallet.com"
            )

        splitPath = bip32_path.split('/')
        if splitPath[0] == 'm':
            splitPath = splitPath[1:]
            bip32_path = bip32_path[2:]
        fingerprint = 0
        if len(splitPath) > 1:
            prevPath = "/".join(splitPath[0:len(splitPath) - 1])
            # print('self.dongleObject.dongle', self.dongleObject.dongle)
            # <btchip.btchipComm.HIDDongleHIDAPI object at 0x11e514fd0>
            try:
                nodeData = self.dongleObject.getWalletPublicKey(prevPath)
            except BTChipException as e:
                if e.sw == 0x6f04:
                    raise BaseException(
                        'error 6f04, Please use Bitcoin mode on your ledger rather than Qtum mode'
                    )
                raise e
            publicKey = compress_public_key(nodeData['publicKey'])
            h = hashlib.new('ripemd160')
            h.update(hashlib.sha256(publicKey).digest())
            fingerprint = unpack(">I", h.digest()[0:4])[0]
        nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
        publicKey = compress_public_key(nodeData['publicKey'])
        depth = len(splitPath)
        lastChild = splitPath[len(splitPath) - 1].split('\'')
        childnum = int(lastChild[0]) if len(
            lastChild) == 1 else 0x80000000 | int(lastChild[0])
        xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'], publicKey,
                                      depth, self.i4b(fingerprint),
                                      self.i4b(childnum))
        return xpub
Beispiel #11
0
    def append_inputs_to_TX(self, utxo, bip32_path):
        self.amount += int(utxo['value'])
        raw_tx = bytearray.fromhex(utxo['raw_tx'])
        
        # parse the raw transaction, so that we can extract the UTXO locking script we refer to
        prev_transaction = bitcoinTransaction(raw_tx)

        utxo_tx_index = utxo['tx_ouput_n']
        if utxo_tx_index < 0 or utxo_tx_index > len(prev_transaction.outputs):
            raise Exception('Incorrect value of outputIndex for UTXO %s-%d' % 
                            (utxo['raw_tx'], utxo['tx_ouput_n']))
        
        trusted_input = self.chip.getTrustedInput(prev_transaction, utxo_tx_index)
        self.trusted_inputs.append(trusted_input)
        
        # Hash check
        curr_pubkey = compress_public_key(self.chip.getWalletPublicKey(bip32_path)['publicKey'])
        pubkey_hash = bin_hash160(curr_pubkey)
        pubkey_hash_from_script = extract_pkh_from_locking_script(prev_transaction.outputs[utxo_tx_index].script)
        if pubkey_hash != pubkey_hash_from_script:
            text = "Error: The hashes for the public key for the BIP32 path, and the UTXO locking script do not match."
            text += "Your signed transaction will not be validated by the network.\n"
            text += "pubkey_hash: %s\n" % pubkey_hash.hex()
            text += "pubkey_hash_from_script: %s\n" % pubkey_hash_from_script.hex()
            printDbg(text)

        self.arg_inputs.append({
            'locking_script': prev_transaction.outputs[utxo['tx_ouput_n']].script,
            'pubkey': curr_pubkey,
            'bip32_path': bip32_path,
            'outputIndex': utxo['tx_ouput_n'],
            'txid': utxo['tx_hash']
        })
Beispiel #12
0
    def scanForBip32(self, account, address, starting_spath=0, spath_count=10, isTestnet=False):
        found = False
        spath = -1
        
        printOK("Scanning for Bip32 path of address: %s" % address)
        for i in range(starting_spath, starting_spath+spath_count):
            curr_path = MPATH + "%d'/0/%d" % (account, i)
            printDbg("checking path... %s" % curr_path)
            self.lock.acquire()
            try:
                if not isTestnet:
                    curr_addr = self.chip.getWalletPublicKey(curr_path).get('address')[12:-2]
                else:
                    pubkey = compress_public_key(self.chip.getWalletPublicKey(curr_path).get('publicKey')).hex()          
                    curr_addr = pubkey_to_address(pubkey, isTestnet)     

                             
                if curr_addr == address:
                    found = True
                    spath = i
                    break
                
                sleep(0.01)
            
            except Exception as e:
                err_msg = 'error in scanForBip32'
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
                
            finally:
                self.lock.release()
                
        return (found, spath)
Beispiel #13
0
 def get_xpub(self, bip32_path):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     try:
         if (os.getenv("LEDGER_NATIVE_SEGWIT")
                 is not None) and self.supports_native_segwit():
             xtype = 'segwit'
         elif bip32_path.startswith("m/49'/"):
             if not self.supports_segwit():
                 raise Exception(
                     "Firmware version too old for Segwit support. Please update at https://www.ledgerwallet.com"
                 )
             xtype = 'segwit_p2sh'
         else:
             xtype = 'standard'
         splitPath = bip32_path.split('/')
         if splitPath[0] == 'm':
             splitPath = splitPath[1:]
             bip32_path = bip32_path[2:]
         fingerprint = 0
         if len(splitPath) > 1:
             prevPath = "/".join(splitPath[0:len(splitPath) - 1])
             nodeData = self.dongleObject.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]
         nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         childnum = int(lastChild[0]) if len(
             lastChild) == 1 else 0x80000000 | int(lastChild[0])
         xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'],
                                       publicKey, depth,
                                       self.i4b(fingerprint),
                                       self.i4b(childnum))
         return xpub
     except Exception as e:
         traceback.print_exc(file=sys.stdout)
         #print_error(e)
         return None
Beispiel #14
0
 def scanForPubKey(self, account, spath):
     result = None
     printOK("Scanning for PubKey of address n. %d on account n. %d" % (spath, account))
     curr_path = MPATH + "%d'/0/%d" % (account, spath)
     with self.lock:
         nodeData = self.chip.getWalletPublicKey(curr_path)
         result = compress_public_key(nodeData.get('publicKey')).hex()
     
     return result
def get_address_and_pubkey(client, bip32_path):
    bip32_path.strip()
    if bip32_path.lower().find('m/') >= 0:
        bip32_path = bip32_path[2:]

    nodedata = client.getWalletPublicKey(bip32_path)

    return {
        'address': nodedata.get('address').decode('utf-8'),
        'publicKey': compress_public_key(nodedata.get('publicKey'))
    }
Beispiel #16
0
    def scanForAddress(self, hwAcc, spath, intExt=0, isTestnet=False):
        with self.lock:
            if not isTestnet:
                curr_path = MPATH + "%d'/%d/%d" % (hwAcc, intExt, spath)
                curr_addr = self.chip.getWalletPublicKey(curr_path).get('address')[12:-2]
            else:
                curr_path = MPATH_TESTNET + "%d'/%d/%d" % (hwAcc, intExt, spath)
                pubkey = compress_public_key(self.chip.getWalletPublicKey(curr_path).get('publicKey')).hex()
                curr_addr = pubkey_to_address(pubkey, isTestnet)

        return curr_addr
Beispiel #17
0
    def scanForPubKey(self, account, spath, isTestnet=False):
        hwpath = "%d'/0/%d" % (account, spath)
        if isTestnet:
            curr_path = MPATH_TESTNET + hwpath
        else:
            curr_path = MPATH + hwpath

        with self.lock:
            nodeData = self.chip.getWalletPublicKey(curr_path)

        return compress_public_key(nodeData.get('publicKey')).hex()
Beispiel #18
0
def get_address_and_pubkey(client, bip32_path, show_display=False):
    bip32_path = clean_bip32_path(bip32_path)
    bip32_path.strip()
    if bip32_path.lower().find('m/') >= 0:
        bip32_path = bip32_path[2:]

    nodedata = client.getWalletPublicKey(bip32_path, showOnScreen=show_display)
    return {
        'address': nodedata.get('address').decode('utf-8'),
        'publicKey': compress_public_key(nodedata.get('publicKey'))
    }
Beispiel #19
0
def main():
#	dongle = getDongle(True)
	dongle = getDongle(False)
	app = btchip(dongle)
	mpath = "44'/5'/0'/0"

	for i in range(11):
		addr_path = mpath + '/' + str(i)
		nodedata = app.getWalletPublicKey(addr_path)
		publicKey = compress_public_key(nodedata.get('publicKey'))
		address   = (nodedata.get('address')).decode("utf-8")
		print(addr_path, address)
Beispiel #20
0
 def get_xpub(self, bip32_path):
     self.checkDevice()
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     try:
         if (os.getenv("LEDGER_NATIVE_SEGWIT") is not None) and self.supports_native_segwit():
             xtype = 'segwit'
         elif bip32_path.startswith("m/49'/"):
             if not self.supports_segwit():
                 raise Exception("Firmware version too old for Segwit support. Please update at https://www.ledgerwallet.com")
             xtype = 'segwit_p2sh'
         else:
             xtype = 'standard'
         splitPath = bip32_path.split('/')
         if splitPath[0] == 'm':
             splitPath = splitPath[1:]
             bip32_path = bip32_path[2:]
         fingerprint = 0
         if len(splitPath) > 1:
             prevPath = "/".join(splitPath[0:len(splitPath) - 1])
             nodeData = self.dongleObject.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]
         nodeData = self.dongleObject.getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData['publicKey'])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split('\'')
         childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0])
         xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'], publicKey, depth, self.i4b(fingerprint), self.i4b(childnum))
         return xpub
     except Exception as e:
         traceback.print_exc(file=sys.stdout)
         #print_error(e)
         return None
Beispiel #21
0
    def scanForAddress(self, account, spath, isTestnet=False):
        curr_addr = None
        curr_path = MPATH + "%d'/0/%d" % (account, spath)
        printOK("Scanning for Address n. %d on account n. %d" % (spath, account))
        
        with self.lock:
            if not isTestnet:
                curr_addr = self.chip.getWalletPublicKey(curr_path).get('address')[12:-2]
            else:
                pubkey = compress_public_key(self.chip.getWalletPublicKey(curr_path).get('publicKey')).hex()
                curr_addr = pubkey_to_address(pubkey, isTestnet)

        return curr_addr
Beispiel #22
0
    def scanForPubKey(self, account, spath):
        printOK("Scanning for Address of path_id %s on account n° %s" %
                (str(spath), str(account)))
        curr_path = MPATH + "%d'/0/%d" % (account, spath)
        try:
            nodeData = self.chip.getWalletPublicKey(curr_path)

        except Exception as e:
            err_msg = 'error in scanForPubKey'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            return None

        return compress_public_key(nodeData.get('publicKey')).hex()
Beispiel #23
0
 def get_public_key(self, bip32_path):
     # bip32_path is of the form 44'/0'/1'
     # 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.handler.show_message("Computing master public key")
     try:
         splitPath = bip32_path.split("/")
         if splitPath[0] == "m":
             splitPath = splitPath[1:]
             bip32_path = bip32_path[2:]
         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]
         nodeData = self.get_client().getWalletPublicKey(bip32_path)
         publicKey = compress_public_key(nodeData["publicKey"])
         depth = len(splitPath)
         lastChild = splitPath[len(splitPath) - 1].split("'")
         if len(lastChild) == 1:
             childnum = int(lastChild[0])
         else:
             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)
Beispiel #24
0
def get_address_and_pubkey(hw_session: HWSessionBase,
                           bip32_path,
                           show_display=False):
    bip32_path = clean_bip32_path(bip32_path)
    bip32_path.strip()
    if bip32_path.lower().find('m/') >= 0:
        bip32_path = bip32_path[2:]

    nodedata = hw_session.hw_client.getWalletPublicKey(
        bip32_path, showOnScreen=show_display)
    addr = _ledger_extract_address(nodedata.get('address'))

    return {
        'address': addr,
        'publicKey': compress_public_key(nodedata.get('publicKey'))
    }
Beispiel #25
0
 def scanForPubKey(self, account, spath):
     self.lock.acquire()
     printOK("Scanning for PubKey of address n. %d on account n. %d" % (spath, account))
     curr_path = MPATH + "%d'/0/%d" % (account, spath)
     try:
         nodeData = self.chip.getWalletPublicKey(curr_path)
                   
             
     except Exception as e:
         err_msg = 'error in scanForPubKey'
         printException(getCallerName(), getFunctionName(), err_msg, e.args)
         return None
 
     finally:
         self.lock.release()
     
     return compress_public_key(nodeData.get('publicKey')).hex()
Beispiel #26
0
 def scanForAddress(self, account, spath, isTestnet=False):
     printOK("Scanning for Address n. %d on account n. %d" % (spath, account))
     curr_path = MPATH + "%d'/0/%d" % (account, spath) 
     self.lock.acquire()
     try:
         if not isTestnet:
             curr_addr = self.chip.getWalletPublicKey(curr_path).get('address')[12:-2]
         else:
             pubkey = compress_public_key(self.chip.getWalletPublicKey(curr_path).get('publicKey')).hex()
             curr_addr = pubkey_to_address(pubkey, isTestnet) 
             
                                      
     except Exception as e:
         err_msg = 'error in scanForAddress'
         printException(getCallerName(), getFunctionName(), err_msg, e.args)
         return None
     finally:
         self.lock.release()
     return curr_addr
Beispiel #27
0
 def scanForBip32(self, account, address, starting_spath=0, spath_count=10, isTestnet=False):
     found = False
     spath = -1       
     printOK("Scanning for Bip32 path of address: %s" % address)
     
     for i in range(starting_spath, starting_spath+spath_count):
         curr_path = MPATH + "%d'/0/%d" % (account, i)
         printDbg("checking path... %s" % curr_path)
         with self.lock: 
             if not isTestnet:
                 curr_addr = self.chip.getWalletPublicKey(curr_path).get('address')[12:-2]
             else:
                 pubkey = compress_public_key(self.chip.getWalletPublicKey(curr_path).get('publicKey')).hex()          
                 curr_addr = pubkey_to_address(pubkey, isTestnet)
                          
             if curr_addr == address:
                 found = True
                 spath = i
                 break
             
             sleep(0.01)
         
     return (found, spath)
Beispiel #28
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return
        client = self.get_client()
        self.signing = True
        inputs = []
        inputsPaths = []
        pubKeys = []
        chipInputs = []
        redeemScripts = []
        signatures = []
        preparedTrustedInputs = []
        changePath = ""
        changeAmount = None
        output = None
        outputAmount = None
        p2shTransaction = False
        pin = ""
        self.get_client(
        )  # prompt for the PIN before displaying the dialog if necessary
        rawTx = tx.serialize()
        # Fetch inputs of the transaction to sign
        for txin in tx.inputs():
            if txin.get('is_coinbase'):
                self.give_error(
                    "Coinbase not supported")  # should never happen
            redeemScript = None
            signingPos = -1
            xpub, s = parse_xpubkey(txin['x_pubkeys'][0])
            hwAddress = "%s/%d/%d" % (self.get_derivation()[2:], s[0], s[1])

            if len(txin['pubkeys']) > 1:
                p2shTransaction = True
            if 'redeemScript' in txin:
                redeemScript = txin['redeemScript']
            if p2shTransaction:
                chipPublicKey = compress_public_key(
                    self.get_client().getWalletPublicKey(
                        hwAddress)['publicKey'])
                for currentIndex, key in enumerate(txin['pubkeys']):
                    if chipPublicKey == key.decode('hex'):
                        signingPos = currentIndex
                        break
                if signingPos == -1:
                    self.give_error("No matching key for multisignature input"
                                    )  # should never happen

            inputs.append([
                txin['prev_tx'].raw, txin['prevout_n'], redeemScript,
                txin['prevout_hash'], signingPos
            ])
            inputsPaths.append(hwAddress)
            pubKeys.append(txin['pubkeys'])

        # Sanity check
        if p2shTransaction:
            for txinput in tx.inputs():
                if len(txinput['pubkeys']) < 2:
                    self.give_error(
                        "P2SH / regular input mixed in same transaction not supported"
                    )  # should never happen

        txOutput = var_int(len(tx.outputs()))
        for txout in tx.outputs():
            output_type, addr, amount = txout
            txOutput += int_to_hex(amount, 8)
            script = tx.pay_script(output_type, addr)
            txOutput += var_int(len(script) / 2)
            txOutput += script
        txOutput = txOutput.decode('hex')

        # Recognize outputs - only one output and one change is authorized
        if not p2shTransaction:
            if len(tx.outputs()) > 2:  # should never happen
                self.give_error(
                    "Transaction with more than 2 outputs not supported")
            for i, (_type, address, amount) in enumerate(tx.outputs()):
                assert _type == TYPE_ADDRESS
                change, index = tx.output_info[i]
                if change:
                    changePath = "%s/%d/%d" % (self.get_derivation()[2:],
                                               change, index)
                    changeAmount = amount
                else:
                    output = address
                    outputAmount = amount

        self.handler.show_message(
            _("Confirm Transaction on your Ledger device..."))
        try:
            # Get trusted inputs from the original transactions
            for utxo in inputs:
                if not p2shTransaction:
                    txtmp = bitcoinTransaction(bytearray(
                        utxo[0].decode('hex')))
                    chipInputs.append(self.get_client().getTrustedInput(
                        txtmp, utxo[1]))
                    redeemScripts.append(txtmp.outputs[utxo[1]].script)
                else:
                    tmp = utxo[3].decode('hex')[::-1].encode('hex')
                    tmp += int_to_hex(utxo[1], 4)
                    chipInputs.append({'value': tmp.decode('hex')})
                    redeemScripts.append(bytearray(utxo[2].decode('hex')))

            # Sign all inputs
            firstTransaction = True
            inputIndex = 0
            while inputIndex < len(inputs):
                self.get_client().startUntrustedTransaction(
                    firstTransaction, inputIndex, chipInputs,
                    redeemScripts[inputIndex])
                outputData = self.get_client().finalizeInputFull(txOutput)
                outputData['outputData'] = txOutput
                if firstTransaction:
                    transactionOutput = outputData['outputData']
                if outputData['confirmationNeeded']:
                    # TODO : handle different confirmation types. For the time being only supports keyboard 2FA
                    self.handler.clear_dialog()
                    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:
                        confirmed, p, pin = self.password_dialog()
                        if not confirmed:
                            raise Exception('Aborted by user')
                        pin = pin.encode()
                        #self.plugin.get_client(self, True, True)
                    self.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 BaseException as e:
            traceback.print_exc(file=sys.stdout)
            self.give_error(e, True)
        finally:
            self.handler.clear_dialog()

        # Reformat transaction
        inputIndex = 0
        while inputIndex < len(inputs):
            if p2shTransaction:
                signaturesPack = [signatures[inputIndex]] * len(
                    pubKeys[inputIndex])
                inputScript = get_p2sh_input_script(redeemScripts[inputIndex],
                                                    signaturesPack)
                preparedTrustedInputs.append([
                    ("\x00" * 4) + chipInputs[inputIndex]['value'], inputScript
                ])
            else:
                inputScript = get_regular_input_script(
                    signatures[inputIndex],
                    pubKeys[inputIndex][0].decode('hex'))
                preparedTrustedInputs.append(
                    [chipInputs[inputIndex]['value'], inputScript])
            inputIndex = inputIndex + 1
        updatedTransaction = format_transaction(transactionOutput,
                                                preparedTrustedInputs)
        updatedTransaction = hexlify(updatedTransaction)
        tx.update_signatures(updatedTransaction)
        self.signing = False
Beispiel #29
0
        updatedTransaction = hexlify(updatedTransaction)
        tx.update(updatedTransaction)
        self.client.bad = use2FA
        self.signing = False

    def check_proper_device(self):
        pubKey = DecodeBase58Check(self.master_public_keys["x/0'"])[45:]
        if not self.device_checked:
            waitDialog.start("Checking device")
            try:
                nodeData = self.get_client().getWalletPublicKey("44'/0'/0'")
            except Exception, e:
                self.give_error(e, True)
            finally:
                waitDialog.emit(SIGNAL('dongle_done'))
            pubKeyDevice = compress_public_key(nodeData['publicKey'])
            self.device_checked = True
            if pubKey != pubKeyDevice:
                self.proper_device = False
            else:
                self.proper_device = True

        return self.proper_device

    def password_dialog(self, msg=None):
        if not msg:
            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 open a text editor (on a different computer / device if you believe this computer is compromised) and put your cursor into it, unplug your BTChip and plug it back in.\r\n" \
                    "It should show itself to your computer as a keyboard and output the second factor along with a summary of the transaction it is signing into the text-editor.\r\n\r\n" \
                    "Check that summary and then enter the second factor code here.\r\n" \
Beispiel #30
0
def prepare_transfer_tx(main_ui, utxos_to_spend, dest_address, tx_fee,
                        rawtransactions):
    client = main_ui.hw_client

    # Each of the UTXOs will become an input in the new transaction. For each of those inputs, create
    # a Ledger's 'trusted input', that will be used by the the device to sign a transaction.
    trusted_inputs = []

    # arg_inputs: list of dicts
    #  {
    #    'locking_script': <Locking script of the UTXO used as an input. Used in the process of signing
    #                       transaction.>,
    #    'outputIndex': <index of the UTXO within the previus transaction>,
    #    'txid': <hash of the previus transaction>,
    #    'bip32_path': <BIP32 path of the HW key controlling UTXO's destination>,
    #    'pubkey': <Public key obtained from the HW using the bip32_path.>
    #    'signature' <Signature obtained as a result of processing the input. It will be used as a part of the
    #               unlocking script.>
    #  }
    #  Why do we need a locking script of the previous transaction? When hashing a new transaction before creating its
    #  signature, all placeholders for input's unlocking script has to be filled with locking script of the
    #  corresponding UTXO. Look here for the details:
    #    https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand)
    arg_inputs = []

    # A dictionary mapping bip32 path to a pubkeys obtained from the Ledger device - used to avoid
    # reading it multiple times for the same bip32 path
    bip32_to_address = {}

    amount = 0
    starting = True
    for idx, utxo in enumerate(utxos_to_spend):
        amount += utxo['satoshis']

        raw_tx = bytearray.fromhex(rawtransactions[utxo['txid']])
        if not raw_tx:
            raise Exception("Can't find raw transaction for txid: " +
                            rawtransactions[utxo['txid']])

        # parse the raw transaction, so that we can extract the UTXO locking script we refer to
        prev_transaction = bitcoinTransaction(raw_tx)

        utxo_tx_index = utxo['outputIndex']
        if utxo_tx_index < 0 or utxo_tx_index > len(prev_transaction.outputs):
            raise Exception('Incorrent value of outputIndex for UTXO %s' %
                            str(idx))

        trusted_input = client.getTrustedInput(prev_transaction, utxo_tx_index)
        trusted_inputs.append(trusted_input)

        bip32_path = utxo['bip32_path']
        bip32_path = clean_bip32_path(bip32_path)
        pubkey = bip32_to_address.get(bip32_path)
        if not pubkey:
            pubkey = compress_public_key(
                client.getWalletPublicKey(bip32_path)['publicKey'])
            bip32_to_address[bip32_path] = pubkey
        pubkey_hash = bitcoin.bin_hash160(pubkey)

        # verify if the public key hash of the wallet's bip32 path is the same as specified in the UTXO locking script
        # if they differ, signature and public key we produce and are going to include in the unlocking script won't
        # match the locking script conditions - transaction will be rejected by the network
        pubkey_hash_from_script = extract_pkh_from_locking_script(
            prev_transaction.outputs[utxo_tx_index].script)
        if pubkey_hash != pubkey_hash_from_script:
            logging.error(
                "Error: different public key hashes for the BIP32 path %s (UTXO %s) and the UTXO locking "
                "script. Your signed transaction will not be validated by the network."
                % (bip32_path, str(idx)))

        arg_inputs.append({
            'locking_script':
            prev_transaction.outputs[utxo['outputIndex']].script,
            'pubkey':
            pubkey,
            'bip32_path':
            bip32_path,
            'outputIndex':
            utxo['outputIndex'],
            'txid':
            utxo['txid']
        })

    amount -= int(tx_fee)
    amount = int(amount)
    arg_outputs = [{
        'address': dest_address,
        'valueSat': amount
    }]  # there will be multiple outputs soon

    new_transaction = bitcoinTransaction(
    )  # new transaction object to be used for serialization at the last stage
    new_transaction.version = bytearray([0x01, 0x00, 0x00, 0x00])
    for o in arg_outputs:
        output = bitcoinOutput()
        output.script = compose_tx_locking_script(o['address'])
        output.amount = int.to_bytes(o['valueSat'], 8, byteorder='little')
        new_transaction.outputs.append(output)

    # join all outputs - will be used by Ledger for sigining transaction
    all_outputs_raw = new_transaction.serializeOutputs()

    # sign all inputs on Ledger and add inputs in the new_transaction object for serialization
    for idx, new_input in enumerate(arg_inputs):

        client.startUntrustedTransaction(starting, idx, trusted_inputs,
                                         new_input['locking_script'])
        client.finalizeInputFull(all_outputs_raw)
        sig = client.untrustedHashSign(new_input['bip32_path'], lockTime=0)
        new_input['signature'] = sig

        input = bitcoinInput()
        input.prevOut = bytearray.fromhex(new_input['txid'])[::-1] + \
                        int.to_bytes(new_input['outputIndex'], 4, byteorder='little')
        input.script = bytearray([len(sig)]) + sig + bytearray(
            [0x21]) + new_input['pubkey']
        input.sequence = bytearray([0xFF, 0xFF, 0xFF, 0xFF])
        new_transaction.inputs.append(input)

        starting = False

    new_transaction.lockTime = bytearray([0, 0, 0, 0])

    tx_raw = bytearray(new_transaction.serialize())
    return tx_raw, amount
Beispiel #31
0
        updatedTransaction = hexlify(updatedTransaction)
        tx.update(updatedTransaction)
        self.client.bad = use2FA
        self.signing = False

    def check_proper_device(self):
        pubKey = DecodeBase58Check(self.master_public_keys["x/0'"])[45:]
        if not self.device_checked:
            self.plugin.handler.show_message("Checking device")
            try:
                nodeData = self.get_client().getWalletPublicKey("44'/0'/0'")
            except Exception, e:
                self.give_error(e, True)
            finally:
                self.plugin.handler.stop()
            pubKeyDevice = compress_public_key(nodeData['publicKey'])
            self.device_checked = True
            if pubKey != pubKeyDevice:
                self.proper_device = False
            else:
                self.proper_device = True

        return self.proper_device

    def password_dialog(self, msg=None):
        if not msg:
            msg = _("Do not enter your device PIN here !\r\n\r\n" \
                    "Your MUEhip wants to talk to you and tell you a unique second factor code.\r\n" \
                    "For this to work, please open a text editor (on a different computer / device if you believe this computer is compromised) and put your cursor into it, unplug your MUEhip and plug it back in.\r\n" \
                    "It should show itself to your computer as a keyboard and output the second factor along with a summary of the transaction it is signing into the text-editor.\r\n\r\n" \
                    "Check that summary and then enter the second factor code here.\r\n" \
Beispiel #32
0
    def prepare_transfer_tx_bulk(self, caller, mnodes, dest_address, tx_fee, rawtransactions, useSwiftX=False):
        # For each UTXO create a Ledger 'trusted input'
        self.trusted_inputs = []
        #    https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand)
        self.arg_inputs = []
        self.amount = 0
        self.lock.acquire()
        num_of_sigs = sum([len(mnode['utxos']) for mnode in mnodes])
        curr_utxo_checked = 0
        try:
            for i, mnode in enumerate(mnodes): 
                
                for idx, utxo in enumerate(mnode['utxos']):
                                       
                    self.amount += int(utxo['value'])
                    raw_tx = bytearray.fromhex(rawtransactions[utxo['tx_hash']])
    
                    if not raw_tx:
                        raise Exception("Can't find raw transaction for txid: " + rawtransactions[utxo['tx_hash']])
                
                    # parse the raw transaction, so that we can extract the UTXO locking script we refer to
                    prev_transaction = bitcoinTransaction(raw_tx)
    
                    utxo_tx_index = utxo['tx_ouput_n']
                    if utxo_tx_index < 0 or utxo_tx_index > len(prev_transaction.outputs):
                        raise Exception('Incorrect value of outputIndex for UTXO %s' % str(idx))
                
                
                    trusted_input = self.chip.getTrustedInput(prev_transaction, utxo_tx_index)
                    self.trusted_inputs.append(trusted_input)
               
                    # Hash check
                    curr_pubkey = compress_public_key(self.chip.getWalletPublicKey(mnode['path'])['publicKey'])
                    pubkey_hash = bin_hash160(curr_pubkey)
                    pubkey_hash_from_script = extract_pkh_from_locking_script(prev_transaction.outputs[utxo_tx_index].script)
                    if pubkey_hash != pubkey_hash_from_script:
                        text = "Error: The hashes for the public key for the BIP32 path, and the UTXO locking script do not match."
                        text += "Your signed transaction will not be validated by the network.\n"
                        text += "pubkey_hash: %s\n" % pubkey_hash.hex()
                        text += "pubkey_hash_from_script: %s\n" % pubkey_hash_from_script.hex()
                        printDbg(text)
    
                    self.arg_inputs.append({
                        'locking_script': prev_transaction.outputs[utxo['tx_ouput_n']].script,
                        'pubkey': curr_pubkey,
                        'bip32_path': mnode['path'],
                        'outputIndex': utxo['tx_ouput_n'],
                        'txid': utxo['tx_hash']
                    })
                    
                    # completion percent emitted
                    curr_utxo_checked += 1
                    completion = int(95*curr_utxo_checked / num_of_sigs)
                    self.tx_progress.emit(completion)
    
            self.amount -= int(tx_fee)
            self.amount = int(self.amount)
            arg_outputs = [{'address': dest_address, 'valueSat': self.amount}] # there will be multiple outputs soon
            self.new_transaction = bitcoinTransaction()  # new transaction object to be used for serialization at the last stage
            self.new_transaction.version = bytearray([0x01, 0x00, 0x00, 0x00])
            
            self.tx_progress.emit(99)
            
        except Exception:
            raise
        
        finally:
            self.lock.release()
        
        try:
            for o in arg_outputs:
                output = bitcoinOutput()
                output.script = compose_tx_locking_script(o['address'])
                output.amount = int.to_bytes(o['valueSat'], 8, byteorder='little')
                self.new_transaction.outputs.append(output)
        except Exception:
            raise
    
        self.tx_progress.emit(100)
        
        # join all outputs - will be used by Ledger for signing transaction
        self.all_outputs_raw = self.new_transaction.serializeOutputs()

        self.mBox2 = QMessageBox(caller)
        self.messageText = "<p>Confirm transaction on your device, with the following details:</p>"
        #messageText += "From bip32_path: <b>%s</b><br><br>" % str(bip32_path)
        self.messageText += "<p>Payment to:<br><b>%s</b></p>" % dest_address
        self.messageText += "<p>Net amount:<br><b>%s</b> PIV</p>" % str(round(self.amount / 1e8, 8))
        if useSwiftX:
            self.messageText += "<p>Fees (SwiftX flat rate):<br><b>%s</b> PIV<p>" % str(round(int(tx_fee) / 1e8, 8))
        else:
            self.messageText += "<p>Fees:<br><b>%s</b> PIV<p>" % str(round(int(tx_fee) / 1e8, 8))
        messageText = self.messageText + "Signature Progress: 0 %" 
        self.mBox2.setText(messageText)
        self.mBox2.setIconPixmap(caller.tabMain.ledgerImg.scaledToHeight(200, Qt.SmoothTransformation))
        self.mBox2.setWindowTitle("CHECK YOUR LEDGER")
        self.mBox2.setStandardButtons(QMessageBox.NoButton)
        self.mBox2.setMaximumWidth(500)
        self.mBox2.show()
                
        ThreadFuns.runInThread(self.signTxSign, (), self.signTxFinish)
    def signTx(self, device, bip32_path, utxos_to_spend, dest_address, tx_fee,
               rawtransactions):
        # For each UTXO create a Ledger 'trusted input'
        self.trusted_inputs = []
        #    https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand)
        self.arg_inputs = []

        self.amount = 0
        for idx, utxo in enumerate(utxos_to_spend):

            self.amount += int(utxo['value'])

            raw_tx = bytearray.fromhex(rawtransactions[utxo['tx_hash']])

            if not raw_tx:
                raise Exception("Can't find raw transaction for txid: " +
                                rawtransactions[utxo['tx_hash']])

            # parse the raw transaction, so that we can extract the UTXO locking script we refer to
            prev_transaction = bitcoinTransaction(raw_tx)

            utxo_tx_index = utxo['tx_ouput_n']
            if utxo_tx_index < 0 or utxo_tx_index > len(
                    prev_transaction.outputs):
                raise Exception('Incorrect value of outputIndex for UTXO %s' %
                                str(idx))

            trusted_input = self.device.chip.getTrustedInput(
                prev_transaction, utxo_tx_index)
            self.trusted_inputs.append(trusted_input)

            # Hash check
            curr_pubkey = compress_public_key(
                device.chip.getWalletPublicKey(bip32_path)['publicKey'])
            pubkey_hash = bin_hash160(curr_pubkey)
            pubkey_hash_from_script = extract_pkh_from_locking_script(
                prev_transaction.outputs[utxo_tx_index].script)
            if pubkey_hash != pubkey_hash_from_script:
                text = "Error: different public key hashes for the BIP32 path and the UTXO"
                text += "locking script. Your signed transaction will not be validated by the network.\n"
                text += "pubkey_hash: %s\n" % str(pubkey_hash)
                text += "pubkey_hash_from_script: %s\n" % str(
                    pubkey_hash_from_script)
                print(text)

            self.arg_inputs.append({
                'locking_script':
                prev_transaction.outputs[utxo['tx_ouput_n']].script,
                'pubkey':
                curr_pubkey,
                'bip32_path':
                bip32_path,
                'outputIndex':
                utxo['tx_ouput_n'],
                'txid':
                utxo['tx_hash']
            })

        self.amount -= int(tx_fee)
        self.amount = int(self.amount)
        arg_outputs = [{
            'address': dest_address,
            'valueSat': self.amount
        }]  # there will be multiple outputs soon
        self.new_transaction = bitcoinTransaction(
        )  # new transaction object to be used for serialization at the last stage
        self.new_transaction.version = bytearray([0x01, 0x00, 0x00, 0x00])
        try:
            for o in arg_outputs:
                output = bitcoinOutput()
                output.script = compose_tx_locking_script(o['address'])
                output.amount = int.to_bytes(o['valueSat'],
                                             8,
                                             byteorder='little')
                self.new_transaction.outputs.append(output)
        except Exception:
            raise
        # join all outputs - will be used by Ledger for signing transaction
        self.all_outputs_raw = self.new_transaction.serializeOutputs()

        starting = True
        # sign all inputs on Ledger and add inputs in the self.new_transaction object for serialization
        for idx, new_input in enumerate(self.arg_inputs):
            device.chip.startUntrustedTransaction(starting, idx,
                                                  self.trusted_inputs,
                                                  new_input['locking_script'])

            device.chip.finalizeInputFull(self.all_outputs_raw)

            sig = device.chip.untrustedHashSign(new_input['bip32_path'],
                                                lockTime=0)

            new_input['signature'] = sig
            inputTx = bitcoinInput()
            inputTx.prevOut = bytearray.fromhex(
                new_input['txid'])[::-1] + int.to_bytes(
                    new_input['outputIndex'], 4, byteorder='little')

            inputTx.script = bytearray([len(sig)]) + sig + bytearray(
                [0x21]) + new_input['pubkey']

            inputTx.sequence = bytearray([0xFF, 0xFF, 0xFF, 0xFF])

            self.new_transaction.inputs.append(inputTx)

            starting = False

            self.new_transaction.lockTime = bytearray([0, 0, 0, 0])
            self.tx_raw = bytearray(self.new_transaction.serialize())

        if self.tx_raw is not None:
            return (self.tx_raw, str(round(self.amount / 1e8, 8)))
        else:
            # transaction refused by user
            return (None, "")
        updatedTransaction = hexlify(updatedTransaction)
        tx.update(updatedTransaction)
        self.client.bad = use2FA
        self.signing = False

    def check_proper_device(self):
        pubKey = DecodeBase58Check(self.master_public_keys["x/0'"])[45:]
        if not self.device_checked:
            self.plugin.handler.show_message("Checking device")
            try:
                nodeData = self.get_client().getWalletPublicKey("44'/2'/0'")
            except Exception, e:
                self.give_error(e, True)
            finally:
                self.plugin.handler.stop()
            pubKeyDevice = compress_public_key(nodeData["publicKey"])
            self.device_checked = True
            if pubKey != pubKeyDevice:
                self.proper_device = False
            else:
                self.proper_device = True

        return self.proper_device

    def password_dialog(self, msg=None):
        if not msg:
            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 open a text editor (on a different computer / device if you believe this computer is compromised) and put your cursor into it, unplug your Ledger Wallet and plug it back in.\r\n"
                "It should show itself to your computer as a keyboard and output the second factor along with a summary of the transaction it is signing into the text-editor.\r\n\r\n"
Beispiel #35
0
def sign_tx(hw_session: HwSessionInfo,
            utxos_to_spend: List[wallet_common.UtxoType],
            tx_outputs: List[wallet_common.TxOutputType], tx_fee):
    client = hw_session.hw_client
    rawtransactions = {}
    decodedtransactions = {}

    # Each of the UTXOs will become an input in the new transaction. For each of those inputs, create
    # a Ledger's 'trusted input', that will be used by the the device to sign a transaction.
    trusted_inputs = []

    # arg_inputs: list of dicts
    #  {
    #    'locking_script': <Locking script of the UTXO used as an input. Used in the process of signing
    #                       transaction.>,
    #    'outputIndex': <index of the UTXO within the previus transaction>,
    #    'txid': <hash of the previus transaction>,
    #    'bip32_path': <BIP32 path of the HW key controlling UTXO's destination>,
    #    'pubkey': <Public key obtained from the HW using the bip32_path.>
    #    'signature' <Signature obtained as a result of processing the input. It will be used as a part of the
    #               unlocking script.>
    #  }
    #  Why do we need a locking script of the previous transaction? When hashing a new transaction before creating its
    #  signature, all placeholders for input's unlocking script has to be filled with locking script of the
    #  corresponding UTXO. Look here for the details:
    #    https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand)
    arg_inputs = []

    # A dictionary mapping bip32 path to a pubkeys obtained from the Ledger device - used to avoid
    # reading it multiple times for the same bip32 path
    bip32_to_address = {}

    # read previous transactins
    for utxo in utxos_to_spend:
        if utxo.txid not in rawtransactions:
            tx = hw_session.crownd_intf.getrawtransaction(utxo.txid,
                                                          1,
                                                          skip_cache=False)
            if tx and tx.get('hex'):
                tx_raw = tx.get('hex')
            else:
                tx_raw = hw_session.crownd_intf.getrawtransaction(
                    utxo.txid, 0, skip_cache=False)

            if tx_raw:
                rawtransactions[utxo.txid] = tx_raw
            decodedtransactions[utxo.txid] = tx

    amount = 0
    starting = True
    for idx, utxo in enumerate(utxos_to_spend):
        amount += utxo.satoshis

        raw_tx = rawtransactions.get(utxo.txid)
        if not raw_tx:
            raise Exception("Can't find raw transaction for txid: " +
                            utxo.txid)
        else:
            raw_tx = bytearray.fromhex(raw_tx)

        # parse the raw transaction, so that we can extract the UTXO locking script we refer to
        prev_transaction = bitcoinTransaction(raw_tx)

        data = decodedtransactions[utxo.txid]
        dip2_type = data.get("type", 0)
        if data['version'] == 3 and dip2_type != 0:
            # It's a DIP2 special TX with payload

            if "extraPayloadSize" not in data or "extraPayload" not in data:
                raise ValueError("Payload data missing in DIP2 transaction")

            if data["extraPayloadSize"] * 2 != len(data["extraPayload"]):
                raise ValueError(
                    "extra_data_len (%d) does not match calculated length (%d)"
                    %
                    (data["extraPayloadSize"], len(data["extraPayload"]) * 2))
            prev_transaction.extra_data = crown_utils.num_to_varint(
                data["extraPayloadSize"]) + bytes.fromhex(data["extraPayload"])
        else:
            prev_transaction.extra_data = bytes()

        utxo_tx_index = utxo.output_index
        if utxo_tx_index < 0 or utxo_tx_index > len(prev_transaction.outputs):
            raise Exception('Incorrent value of outputIndex for UTXO %s' %
                            str(idx))

        trusted_input = client.getTrustedInput(prev_transaction, utxo_tx_index)
        trusted_inputs.append(trusted_input)

        bip32_path = utxo.bip32_path
        bip32_path = clean_bip32_path(bip32_path)
        pubkey = bip32_to_address.get(bip32_path)
        if not pubkey:
            pubkey = compress_public_key(
                client.getWalletPublicKey(bip32_path)['publicKey'])
            bip32_to_address[bip32_path] = pubkey
        pubkey_hash = bitcoin.bin_hash160(pubkey)

        # verify if the public key hash of the wallet's bip32 path is the same as specified in the UTXO locking script
        # if they differ, signature and public key we produce and are going to include in the unlocking script won't
        # match the locking script conditions - transaction will be rejected by the network
        pubkey_hash_from_script = extract_pkh_from_locking_script(
            prev_transaction.outputs[utxo_tx_index].script)
        if pubkey_hash != pubkey_hash_from_script:
            logging.error(
                "Error: different public key hashes for the BIP32 path %s (UTXO %s) and the UTXO locking "
                "script. Your signed transaction will not be validated by the network."
                % (bip32_path, str(idx)))

        arg_inputs.append({
            'locking_script':
            prev_transaction.outputs[utxo.output_index].script,
            'pubkey':
            pubkey,
            'bip32_path':
            bip32_path,
            'outputIndex':
            utxo.output_index,
            'txid':
            utxo.txid
        })

    amount -= int(tx_fee)
    amount = int(amount)

    new_transaction = bitcoinTransaction(
    )  # new transaction object to be used for serialization at the last stage
    new_transaction.version = bytearray([0x01, 0x00, 0x00, 0x00])
    for out in tx_outputs:
        output = bitcoinOutput()
        output.script = compose_tx_locking_script(
            out.address, hw_session.app_config.crown_network)
        output.amount = int.to_bytes(out.satoshis, 8, byteorder='little')
        new_transaction.outputs.append(output)

    # join all outputs - will be used by Ledger for sigining transaction
    all_outputs_raw = new_transaction.serializeOutputs()

    # sign all inputs on Ledger and add inputs in the new_transaction object for serialization
    for idx, new_input in enumerate(arg_inputs):

        client.startUntrustedTransaction(starting, idx, trusted_inputs,
                                         new_input['locking_script'])
        client.finalizeInputFull(all_outputs_raw)
        sig = client.untrustedHashSign(new_input['bip32_path'], lockTime=0)
        new_input['signature'] = sig

        input = bitcoinInput()
        input.prevOut = bytearray.fromhex(new_input['txid'])[::-1] + \
                        int.to_bytes(new_input['outputIndex'], 4, byteorder='little')
        input.script = bytearray([len(sig)]) + sig + bytearray(
            [0x21]) + new_input['pubkey']
        input.sequence = bytearray([0xFF, 0xFF, 0xFF, 0xFF])
        new_transaction.inputs.append(input)

        starting = False

    new_transaction.lockTime = bytearray([0, 0, 0, 0])

    tx_raw = bytearray(new_transaction.serialize())
    return tx_raw, amount
Beispiel #36
0
    def prepare_transfer_tx(self, caller, bip32_path, utxos_to_spend,
                            dest_address, tx_fee, rawtransactions):
        # For each UTXO create a Ledger 'trusted input'
        self.trusted_inputs = []
        #    https://klmoney.wordpress.com/bitcoin-dissecting-transactions-part-2-building-a-transaction-by-hand)
        self.arg_inputs = []
        self.amount = 0
        for idx, utxo in enumerate(utxos_to_spend):

            self.amount += int(utxo['value'])
            raw_tx = bytearray.fromhex(rawtransactions[utxo['tx_hash']])

            if not raw_tx:
                raise Exception("Can't find raw transaction for txid: " +
                                rawtransactions[utxo['tx_hash']])

            # parse the raw transaction, so that we can extract the UTXO locking script we refer to
            prev_transaction = bitcoinTransaction(raw_tx)

            utxo_tx_index = utxo['tx_ouput_n']
            if utxo_tx_index < 0 or utxo_tx_index > len(
                    prev_transaction.outputs):
                raise Exception('Incorrect value of outputIndex for UTXO %s' %
                                str(idx))

            trusted_input = self.chip.getTrustedInput(prev_transaction,
                                                      utxo_tx_index)
            self.trusted_inputs.append(trusted_input)

            # Hash check
            curr_pubkey = compress_public_key(
                self.chip.getWalletPublicKey(bip32_path)['publicKey'])
            pubkey_hash = bin_hash160(curr_pubkey)
            pubkey_hash_from_script = extract_pkh_from_locking_script(
                prev_transaction.outputs[utxo_tx_index].script)
            if pubkey_hash != pubkey_hash_from_script:
                text = "Error: different public key hashes for the BIP32 path and the UTXO"
                text += "locking script. Your signed transaction will not be validated by the network.\n"
                text += "pubkey_hash: %s\n" % str(pubkey_hash)
                text += "pubkey_hash_from_script: %s\n" % str(
                    pubkey_hash_from_script)
                printDbg(text)

            self.arg_inputs.append({
                'locking_script':
                prev_transaction.outputs[utxo['tx_ouput_n']].script,
                'pubkey':
                curr_pubkey,
                'bip32_path':
                bip32_path,
                'outputIndex':
                utxo['tx_ouput_n'],
                'txid':
                utxo['tx_hash']
            })

        self.amount -= int(tx_fee)
        self.amount = int(self.amount)
        arg_outputs = [{
            'address': dest_address,
            'valueSat': self.amount
        }]  # there will be multiple outputs soon
        self.new_transaction = bitcoinTransaction(
        )  # new transaction object to be used for serialization at the last stage
        self.new_transaction.version = bytearray([0x01, 0x00, 0x00, 0x00])

        try:
            for o in arg_outputs:
                output = bitcoinOutput()
                output.script = compose_tx_locking_script(o['address'])
                output.amount = int.to_bytes(o['valueSat'],
                                             8,
                                             byteorder='little')
                self.new_transaction.outputs.append(output)
        except Exception:
            raise

        # join all outputs - will be used by Ledger for signing transaction
        self.all_outputs_raw = self.new_transaction.serializeOutputs()

        self.mBox2 = QMessageBox(caller)
        messageText = "Check display of your hardware device<br><br><br>"
        messageText += "From bip32_path: <b>%s</b><br><br>" % str(bip32_path)
        messageText += "To PIVX Address: <b>%s</b><br><br>" % dest_address
        messageText += "PIV amount: <b>%s</b><br>" % str(
            round(self.amount / 1e8, 8))
        messageText += "plus PIV for fee: <b>%s</b><br><br>" % str(
            round(int(tx_fee) / 1e8, 8))
        self.mBox2.setText(messageText)
        self.mBox2.setIconPixmap(
            caller.tabMain.ledgerImg.scaledToHeight(200,
                                                    Qt.SmoothTransformation))
        self.mBox2.setWindowTitle("CHECK YOUR LEDGER")
        self.mBox2.setStandardButtons(QMessageBox.NoButton)
        self.mBox2.setMaximumWidth(500)
        self.mBox2.show()

        ThreadFuns.runInThread(self.signTxSign, (), self.signTxFinish)