Example #1
0
 def test_random_access(self):
     p = BIP32Path("m/4h/5h/1/4")
     self.assertEqual(p[0], 4 + BIP32_HARDENED_KEY_OFFSET)
     self.assertEqual(p[1], 5 + BIP32_HARDENED_KEY_OFFSET)
     self.assertEqual(p[2], 1)
     self.assertEqual(p[3], 4)
     p = BIP32Path([0xFFFFFFFF - n for n in range(255)])
     self.assertEqual(p[254], 0xFFFFFF01)
Example #2
0
    def test_from_list(self):
        with self.assertRaises(ValueError):
            BIP32Path([-1])
        with self.assertRaises(ValueError):
            BIP32Path([0xFFFFFFFF + 1])  # more than 32bit
        with self.assertRaises(ValueError):
            # only apostrophe and "h" markers are allowed
            BIP32Path([0xFFFFFFFF, 0, 0x80000000], hardened_marker='b')

        with self.assertRaises(ValueError):
            # too long path
            BIP32Path([0 for _ in range(256)])

        self.assertEqual(str(BIP32Path([0])), "m/0")
        self.assertEqual(str(BIP32Path([])), "m")

        self.assertEqual(
            str(BIP32Path([0xFFFFFFFF, 0x80000001, 1, 0x80000002])),
            "m/2147483647'/1'/1/2'")

        self.assertEqual(
            str(BIP32Path([0xFFFFFFFF, 0x80000001, 1, 2],
                          hardened_marker='h')), "m/2147483647h/1h/1/2")

        self.assertEqual(
            str(
                BIP32Path([n + BIP32_HARDENED_KEY_OFFSET
                           for n in range(128)] + [n for n in range(127)])),
            'm/' + '/'.join("%u'" % n for n in range(128)) + '/' +
            '/'.join("%u" % n for n in range(127)))
Example #3
0
 def test_from_BIP32Path(self) -> None:
     p = BIP32Path("m/4h/5h/1/4")
     self.assertEqual(str(BIP32Path(p)), "m/4h/5h/1/4")
     self.assertEqual(str(BIP32Path(p, hardened_marker="'")), "m/4'/5'/1/4")
     p = BIP32Path("m/4'/5'/1/4")
     self.assertEqual(str(BIP32Path(p)), "m/4'/5'/1/4")
     self.assertEqual(str(BIP32Path(p, hardened_marker='h')), "m/4h/5h/1/4")
     p = BIP32Path("4'/5'/1/4")
     self.assertEqual(str(BIP32Path(p)), "4'/5'/1/4")
     self.assertEqual(str(BIP32Path(p, hardened_marker='h')), "4h/5h/1/4")
Example #4
0
 def assemble_psbt_change_output(self, psbt: PartiallySignedTransaction):
     change_output = psbt.outputs[1]
     last_change_address = self.watch_only_wallet.last_change_address
     address_derivation_info = PSBT_KeyDerivationInfo(
         self.watch_only_wallet.master_fingerprint,
         BIP32Path(last_change_address.key_path))
     change_output.derivation_map[
         last_change_address.pub_key] = address_derivation_info
Example #5
0
    def test_path_as_list(self) -> None:
        self.assertEqual(list(BIP32Path('m/0')), [0])
        self.assertEqual(list(BIP32Path('0')), [0])

        self.assertEqual(
            list(BIP32Path("m/4h/5/1h")),
            [4 + BIP32_HARDENED_KEY_OFFSET, 5, 1 + BIP32_HARDENED_KEY_OFFSET])

        self.assertEqual(list(BIP32Path("m/0'/2147483647'/1/10")),
                         [BIP32_HARDENED_KEY_OFFSET, 0xFFFFFFFF, 1, 10])

        self.assertEqual(
            list(
                BIP32Path('m/' + '/'.join("%u'" % n
                                          for n in range(128)) + '/' +
                          '/'.join("%u" % (BIP32_HARDENED_KEY_OFFSET - n - 1)
                                   for n in range(127)))),
            [n + BIP32_HARDENED_KEY_OFFSET for n in range(128)] +
            [BIP32_HARDENED_KEY_OFFSET - n - 1 for n in range(127)])
Example #6
0
        def T(base_xpub, expected_child, path):
            xpub = CBitcoinExtPubKey(base_xpub)
            for child_num in path:
                xpub = xpub.derive(child_num)

            self.assertEqual(str(CBitcoinExtPubKey.from_bytes(xpub)),
                             expected_child)

            xpub = CBitcoinExtPubKey(base_xpub).derive_path(
                str(BIP32Path(path)))
            self.assertEqual(str(CBitcoinExtPubKey.from_bytes(xpub)),
                             expected_child)
Example #7
0
    def test_standard_bip32_vectors(self):
        for vector in BIP32_TEST_VECTORS:
            _, seed = vector[0]
            base_key = CBitcoinExtKey.from_seed(x(seed))
            key = base_key
            path = []
            for xpub, xpriv, child_num in vector[1:]:
                self.assertEqual(xpub, str(key.neuter()))
                self.assertEqual(xpriv, str(key))
                key = key.derive(child_num)
                path.append(child_num)

            key_from_path = base_key.derive_path(str(BIP32Path(path)))
            self.assertEqual(key, key_from_path)
Example #8
0
        def T(base_xpub: str, expected_child: str, path: List[int]) -> None:
            xpub = CBitcoinExtPubKey(base_xpub)
            self.assertEqual(xpub.parent_fp, b'\x00\x00\x00\x00')
            for child_num in path:
                parent_fp = xpub.fingerprint
                xpub = xpub.derive(child_num)
                self.assertEqual(xpub.parent_fp, parent_fp)

            self.assertEqual(str(CBitcoinExtPubKey.from_bytes(xpub)),
                             expected_child)

            xpub = CBitcoinExtPubKey(base_xpub).derive_path(
                str(BIP32Path(path)))
            self.assertEqual(str(CBitcoinExtPubKey.from_bytes(xpub)),
                             expected_child)
Example #9
0
    def test_random_access(self) -> None:
        p = BIP32Path("m/4h/5h/1/4")
        self.assertEqual(p[0], 4 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(p[1], 5 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(p[2], 1)
        self.assertEqual(p[3], 4)
        p = BIP32Path([0xFFFFFFFF - n for n in range(255)])
        self.assertEqual(p[254], 0xFFFFFF01)

        pt = BIP32PathTemplate("m/4h/5h/[1-2]/[4,7]")
        self.assertEqual(pt[0][0][0], 4 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(pt[0][0][1], 4 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(pt[1][0][0], 5 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(pt[1][0][1], 5 + BIP32_HARDENED_KEY_OFFSET)
        self.assertEqual(pt[2][0][0], 1)
        self.assertEqual(pt[2][0][1], 2)
        self.assertEqual(pt[3][0][0], 4)
        self.assertEqual(pt[3][0][1], 4)
        self.assertEqual(pt[3][1][0], 7)
        self.assertEqual(pt[3][1][1], 7)
        pt = BIP32PathTemplate([[(0xFFFFFFFF - n, 0xFFFFFFFF - n)]
                                for n in range(255)])
        self.assertEqual(pt[254][0][0], 0xFFFFFF01)
        self.assertEqual(pt[254][0][1], 0xFFFFFF01)
Example #10
0
 def assembe_psbt_inputs(self, psbt: PartiallySignedTransaction,
                         utxos: List[Utxo]):
     for i, psbt_input in enumerate(psbt.inputs):
         utxo = utxos[i]
         address = self.watch_only_wallet.get_address(utxo.address)
         address_derivation_info = PSBT_KeyDerivationInfo(
             self.watch_only_wallet.master_fingerprint,
             BIP32Path(address.key_path))
         psbt_input.derivation_map[
             address.pub_key] = address_derivation_info
         if utxo.is_witness_utxo:
             psbt_input.set_utxo(utxo.tx_out, psbt.unsigned_tx)
         else:
             transaction = self.blockchain_client.get_transaction(
                 utxo.tx_hash)
             psbt_input.set_utxo(transaction, psbt.unsigned_tx)
Example #11
0
    def test_standard_bip32_vectors(self) -> None:
        for vector in BIP32_TEST_VECTORS:
            _, seed, _ = vector[0]
            base_key = CBitcoinExtKey.from_seed(x(seed))
            self.assertEqual(base_key.parent_fp, b'\x00\x00\x00\x00')
            key = base_key
            path = []
            for xpub, xpriv, child_num in vector[1:]:
                self.assertEqual(xpub, str(key.neuter()))
                self.assertEqual(xpriv, str(key))
                parent_fp = key.fingerprint
                key = key.derive(child_num)
                self.assertEqual(key.parent_fp, parent_fp)
                path.append(child_num)

            key_from_path = base_key.derive_path(str(BIP32Path(path)))
            self.assertEqual(key, key_from_path)
Example #12
0
            except ValueError:
                pass
        else:
            print("ERROR: specified key does not appear to be valid")
            sys.exit(-1)

    path_str = sys.argv[1]

    if not path_str.startswith('m'):
        # Allow users to omit path prefix
        if path_str == '':
            path_str = 'm'
        else:
            path_str = 'm/' + path_str.lstrip('/')

    path = BIP32Path(path_str)

    if len(path) == 0:
        # NOTE: xkey.derive_path() method will raise ValueError
        # on empty path, to guard against bugs:
        #   if there is nothing to derive, why call derive_path() ?
        print('ERROR: nothing to derive, path is empty.')
        sys.exit(-1)

    for n in path:
        print("child number: 0x{:08x}".format(n))
        xkey = xkey.derive(n)
        if isinstance(xkey, CBitcoinExtKey):
            print("xpriv:", xkey)

            # Note:
Example #13
0
    def test(self) -> None:
        xpriv1 = CCoinExtKey(
            'xprv9s21ZrQH143K4TFwadu5VoGfAChTWXUw49YyTWE8SRqC9ZC9AQpHspzgbAcScTmC4MURiMT7pmCbci5oKbWijJmARiUeRiLXYehCtsoVdYf'
        )
        xpriv2 = CCoinExtKey(
            'xprv9uZ4jKNZFfGEQTTunEuy2cLQMckzuy5saCmiKuxYJgHX5pGFCx3KQ8mTkSfuLNaWGNQ9LKCg5YzUihxoQv493ErnkcaS3q1udx9X8WZbwZc'
        )
        priv1 = CCoinKey(
            'L27zAtDgjDC34sG5ZSey1wvdZ9JyZsNnvZEwbbZYWUYXXQtgri5R')
        xpub1 = CCoinExtPubKey(
            'xpub69b6hm71WMe1PGpgUmaDPkbxYoTzpmswX8KGeinv7SPRcKT22RdMM4416kqtEUuXqXCAi7oGx7tHwCRTd3JHatE3WX1Zms6Lgj5mrbFyuro'
        )
        xpub1.assign_derivation_info(
            KeyDerivationInfo(xpub1.parent_fp, BIP32Path('m/0')))
        pub1 = CPubKey(
            x('03b0fe9cfc88fed9fcecf9dcb7bb5c90dd1a4500f4cfc5c854ffc8e54d639d6bc5'
              ))

        kstore = KeyStore(
            external_privkey_lookup=(lambda key_id, dinfo: priv1
                                     if key_id == priv1.pub.key_id else None),
            external_pubkey_lookup=(lambda key_id, dinfo: pub1
                                    if key_id == pub1.key_id else None))
        self.assertEqual(kstore.get_privkey(priv1.pub.key_id), priv1)
        self.assertEqual(kstore.get_pubkey(pub1.key_id), pub1)
        self.assertEqual(kstore.get_pubkey(priv1.pub.key_id), priv1.pub)

        kstore = KeyStore(xpriv1,
                          priv1,
                          xpub1,
                          pub1,
                          require_path_templates=False)
        self.assertEqual(kstore.get_privkey(priv1.pub.key_id), priv1)
        self.assertEqual(kstore.get_pubkey(priv1.pub.key_id), priv1.pub)
        self.assertEqual(kstore.get_pubkey(pub1.key_id), pub1)

        # check that no-derivation lookup for (priv, pub) of extended keys
        # does not return anything if derivation is not supplied,
        # but returns pubkey when empty path is supplied
        self.assertEqual(kstore.get_privkey(xpriv1.pub.key_id), None)
        self.assertEqual(
            kstore.get_privkey(
                xpriv1.pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint, BIP32Path("m"))),
            xpriv1.priv)
        self.assertEqual(kstore.get_pubkey(xpriv1.pub.key_id), None)
        self.assertEqual(
            kstore.get_pubkey(
                xpriv1.pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint, BIP32Path("m"))),
            xpriv1.pub)

        # can't find xpub1's pub without derivation
        self.assertEqual(kstore.get_pubkey(xpub1.pub.key_id), None)

        # can find with correct derivation info supplied
        self.assertEqual(
            kstore.get_pubkey(
                xpub1.pub.key_id,
                KeyDerivationInfo(xpub1.parent_fp, BIP32Path("m/0"))),
            xpub1.pub)

        # but not with incorrect derivation info
        self.assertEqual(
            kstore.get_pubkey(
                xpub1.pub.key_id,
                KeyDerivationInfo(xpub1.parent_fp, BIP32Path("m"))), None)

        # check longer derivations
        self.assertEqual(
            kstore.get_privkey(xpriv1.derive_path("0'/1'/2'").pub.key_id),
            None)
        self.assertEqual(
            kstore.get_privkey(
                xpriv1.derive_path("0'/1'/2'").pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint,
                                  BIP32Path("m/0'/1'/2'"))),
            xpriv1.derive_path("0'/1'/2'").priv)
        self.assertEqual(
            kstore.get_pubkey(
                xpriv1.derive_path("0'/1'/2'").pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint,
                                  BIP32Path("m/0'/1'/2'"))),
            xpriv1.derive_path("0'/1'/2'").pub)

        self.assertEqual(
            kstore.get_pubkey(
                xpub1.derive_path("0/1/2").pub.key_id,
                KeyDerivationInfo(xpub1.parent_fp, BIP32Path('m/0/0/1/2'))),
            xpub1.derive_path("0/1/2").pub)

        path = BIP32Path("0'/1'/2'")
        derived_xpub = xpriv2.derive_path(path).neuter()
        derived_pub = derived_xpub.derive_path('3/4/5').pub
        self.assertEqual(kstore.get_pubkey(derived_pub.key_id), None)
        kstore.add_key(derived_xpub)
        self.assertEqual(
            kstore.get_pubkey(
                derived_pub.key_id,
                KeyDerivationInfo(xpriv2.parent_fp,
                                  BIP32Path("m/0/0'/1'/2'/3/4/5"))),
            derived_pub)

        kstore.add_key(xpriv2)

        derived_pub = xpriv2.derive_path('3h/4h/5h').pub
        self.assertEqual(
            kstore.get_pubkey(
                derived_pub.key_id,
                KeyDerivationInfo(xpriv2.parent_fp,
                                  BIP32Path("m/0/3'/4'/5'"))), derived_pub)

        derived_priv = xpriv2.derive_path('3h/4h/5h').priv
        self.assertEqual(
            kstore.get_privkey(
                derived_priv.pub.key_id,
                KeyDerivationInfo(xpriv2.parent_fp,
                                  BIP32Path("m/0/3'/4'/5'"))), derived_priv)

        # check that .remove_key() works
        kstore.remove_key(xpriv1)
        kstore.remove_key(xpub1)
        kstore.remove_key(priv1)
        kstore.remove_key(pub1)

        self.assertEqual(kstore.get_privkey(priv1.pub.key_id), None)
        self.assertEqual(kstore.get_pubkey(pub1.key_id), None)
        self.assertEqual(
            kstore.get_privkey(
                xpriv1.derive_path("0'/1'/2'").pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint,
                                  BIP32Path("m/0'/1'/2'"))), None)
        self.assertEqual(
            kstore.get_pubkey(xpub1.derive_path("0/1/2").pub.key_id), None)
Example #14
0
    def test_path_template_enforcement(self) -> None:
        xpriv1 = CCoinExtKey(
            'xprv9s21ZrQH143K4TFwadu5VoGfAChTWXUw49YyTWE8SRqC9ZC9AQpHspzgbAcScTmC4MURiMT7pmCbci5oKbWijJmARiUeRiLXYehCtsoVdYf'
        )
        xpriv2 = CCoinExtKey(
            'xprv9s21ZrQH143K3QgBvK4tkeHuvuWc6KETTTcgGQ4NmW7g16AtCPV4hZpujiimpLM9ivFPgsMdNNVuVUnDwChutxczNKYHzP1Mo5HuqG7CNYv'
        )
        assert xpriv2.derivation_info
        assert len(xpriv2.derivation_info.path) == 0
        priv1 = CCoinKey(
            'L27zAtDgjDC34sG5ZSey1wvdZ9JyZsNnvZEwbbZYWUYXXQtgri5R')
        xpub1 = CCoinExtPubKey(
            'xpub69b6hm71WMe1PGpgUmaDPkbxYoTzpmswX8KGeinv7SPRcKT22RdMM4416kqtEUuXqXCAi7oGx7tHwCRTd3JHatE3WX1Zms6Lgj5mrbFyuro'
        )
        xpub2 = xpriv2.derive(333).neuter()
        xpub1.assign_derivation_info(
            KeyDerivationInfo(xpub1.parent_fp, BIP32Path('m/0')))
        pub1 = CPubKey(
            x('03b0fe9cfc88fed9fcecf9dcb7bb5c90dd1a4500f4cfc5c854ffc8e54d639d6bc5'
              ))

        xpub3 = xpub1.derive(0)
        xpub3.assign_derivation_info(
            KeyDerivationInfo(x('abcdef10'), BIP32Path('m/0/0')))

        # No error when require_path_templates is not set
        KeyStore(xpriv1,
                 xpriv2,
                 priv1,
                 xpub1,
                 pub1,
                 require_path_templates=False)

        with self.assertRaisesRegex(ValueError,
                                    'only make sense for extended keys'):
            KeyStore((priv1, BIP32PathTemplate('')))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'only make sense for extended keys'):
            KeyStore((pub1, [BIP32PathTemplate('')]))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'path templates must be specified'):
            KeyStore(xpriv1)
        with self.assertRaisesRegex(ValueError,
                                    'path templates must be specified'):
            KeyStore(xpub1)

        # same but via add_key
        ks = KeyStore()
        with self.assertRaisesRegex(ValueError,
                                    'only make sense for extended keys'):
            ks.add_key((priv1, BIP32PathTemplate('')))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'only make sense for extended keys'):
            ks.add_key((pub1, [BIP32PathTemplate('')]))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'path templates list is empty'):
            ks.add_key((pub1, []))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'only make sense for extended keys'):
            ks.add_key((pub1, ''))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'index template format is not valid'):
            ks.add_key((xpub1, 'abc'))  # type: ignore
        with self.assertRaisesRegex(TypeError,
                                    'is expected to be an instance of '):
            ks.add_key((xpub1, [10]))  # type: ignore
        with self.assertRaisesRegex(ValueError,
                                    'path templates must be specified'):
            ks.add_key(xpriv1)
        with self.assertRaisesRegex(ValueError,
                                    'path templates must be specified'):
            ks.add_key(xpub1)

        # No error when path templates are specified for extended keys
        ks = KeyStore(
            (xpriv1, BIP32PathTemplate('m')),
            (xpriv2, 'm/[44,49,84]h/0h/0h/[0-1]/*'),
            (xpub1, ''),  # '' same as BIP32PathTemplate('')
            (xpub2, ['0/1', 'm/333/3/33']),
            (xpub3, BIP32PathTemplate('m/0/0/1')),
            priv1,
            pub1)

        self.assertEqual(ks.get_privkey(priv1.pub.key_id), priv1)
        self.assertEqual(ks.get_pubkey(pub1.key_id), pub1)

        # still can find non-extended priv even if derivation info is
        # specified, because there's exact match.
        self.assertEqual(
            ks.get_privkey(priv1.pub.key_id,
                           KeyDerivationInfo(xpriv1.parent_fp,
                                             BIP32Path("m"))), priv1)
        self.assertEqual(ks.get_pubkey(pub1.key_id), pub1)

        # can't find without derivation specified
        self.assertEqual(ks.get_privkey(xpriv1.pub.key_id), None)
        # but can find with derivation specified
        self.assertEqual(
            ks.get_privkey(
                xpriv1.pub.key_id,
                KeyDerivationInfo(xpriv1.fingerprint, BIP32Path('m'))),
            xpriv1.priv)

        # can't find without derivation specified
        self.assertEqual(ks.get_pubkey(xpub1.pub.key_id), None)

        # can find with derivation specified
        self.assertEqual(
            ks.get_pubkey(xpub1.pub.key_id,
                          KeyDerivationInfo(xpub1.parent_fp,
                                            BIP32Path('m/0'))), xpub1.pub)

        # exception when derivation goes beyond template
        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub1.derive(1).pub.key_id,
                KeyDerivationInfo(xpub1.parent_fp, BIP32Path('m/0/1')))

        # success when template allows
        self.assertEqual(
            ks.get_pubkey(
                xpub3.derive(1).pub.key_id,
                KeyDerivationInfo(x('abcdef10'), BIP32Path('m/0/0/1'))),
            xpub3.derive(1).pub)

        # fails when template not allows
        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub3.derive(2).pub.key_id,
                KeyDerivationInfo(x('abcdef10'), BIP32Path('m/0/0/2')))

        long_path = BIP32Path(
            "m/43435/646/5677/5892/58885/2774/9943/75532/8888")

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_privkey(
                xpriv2.derive_path(long_path).pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint, long_path))

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_privkey(
                xpriv2.derive_path("44'/0'/0'/3/25").pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/44h/0h/0h/3/25')))

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_privkey(
                xpriv2.derive_path("44'/0'/0'/0/1'").pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/44h/0h/0h/0/1h')))

        self.assertEqual(
            ks.get_privkey(
                xpriv2.derive_path("44'/0'/0'/1/25").pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/44h/0h/0h/1/25'))),
            xpriv2.derive_path("44'/0'/0'/1/25").priv)

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub2.derive_path('0').pub.key_id,
                KeyDerivationInfo(xpub2.parent_fp, BIP32Path('m/333/0')))

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub2.derive_path('3/34').pub.key_id,
                KeyDerivationInfo(xpub2.parent_fp, BIP32Path('m/333/3/34')))

        self.assertEqual(
            ks.get_pubkey(
                xpub2.derive_path('3/33').pub.key_id,
                KeyDerivationInfo(xpub2.parent_fp, BIP32Path('m/333/3/33'))),
            xpub2.derive_path('3/33').pub)

        xpub49 = xpriv2.derive_path("m/49'/0'/0'/0").neuter()

        with self.assertRaisesRegex(ValueError, 'must specify full path'):
            ks = KeyStore(
                xpriv2,
                xpub49,
                default_path_template='[44,49,84]h/0h/0h/[0-1]/[0-50000]')

        ks = KeyStore(
            xpriv2,
            xpub49,
            default_path_template='m/[44,49,84]h/0h/0h/[0-1]/[0-50000]')

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_privkey(
                xpriv2.derive_path(long_path).pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint, long_path))

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_privkey(
                xpriv2.derive_path("44'/0'/0'/1/50001").pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/44h/0h/0h/1/50001')))

        self.assertEqual(
            ks.get_privkey(
                xpriv2.derive_path("44'/0'/0'/1/25").pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/44h/0h/0h/1/25'))),
            xpriv2.derive_path("44'/0'/0'/1/25").priv)

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub49.derive_path('50001').pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/49h/0h/0h/0/50001')))

        with self.assertRaises(BIP32PathTemplateViolation):
            ks.get_pubkey(
                xpub49.derive_path('50000/3').pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/49h/0h/0h/0/50000/3')))

        self.assertEqual(
            ks.get_pubkey(
                xpub49.derive_path('50000').pub.key_id,
                KeyDerivationInfo(xpriv2.fingerprint,
                                  BIP32Path('m/49h/0h/0h/0/50000'))),
            xpub49.derive_path('50000').pub)
Example #15
0
    def test_BIP32PathTemplate_match_path(self) -> None:
        t_partial = BIP32PathTemplate("4'/5'/1/[3,4,5-50]")
        t_full = BIP32PathTemplate("m/4'/5'/1/[3,4,5-50]")

        for v in [3, 4] + list(range(5, 50)):
            self.assertTrue(t_partial.match_path(BIP32Path(f"4'/5'/1/{v}")))

        for v in [3, 4] + list(range(5, 50)):
            self.assertTrue(t_full.match_path(BIP32Path(f"m/4'/5'/1/{v}")))

        self.assertFalse(t_full.match_path(BIP32Path("4'/5'/1/3")))
        self.assertFalse(t_partial.match_path(BIP32Path("m/4'/5'/1/3")))

        self.assertFalse(t_full.match_path(BIP32Path("m/4'/5'/1")))
        self.assertFalse(t_partial.match_path(BIP32Path("4'/5'/1")))

        self.assertFalse(t_full.match_path(BIP32Path("m/4'/5'/1/3/1")))
        self.assertFalse(t_partial.match_path(BIP32Path("4'/5'/1/3/1")))

        self.assertTrue(
            BIP32PathTemplate("m/4'/5'/1/[3,4,5-50]/*").match_path(
                BIP32Path("m/4'/5'/1/3/1")))

        self.assertTrue(
            BIP32PathTemplate("4h/5h/1h/[3,4,5-50]h/*h").match_path(
                BIP32Path("4'/5'/1'/3'/323452'")))

        self.assertFalse(
            BIP32PathTemplate("4h/5h/1h/[3,4,5-50]h/*h").match_path(
                BIP32Path("4'/5'/1'/3/323452'")))

        self.assertFalse(
            BIP32PathTemplate("4h/5h/1h/[3,4,5-50]h/*h").match_path(
                BIP32Path("4'/5'/1'/3'/323452")))

        self.assertTrue(
            BIP32PathTemplate("*h").match_path(BIP32Path("323452h")))
        self.assertTrue(
            BIP32PathTemplate("[0-100,200-300]h").match_path(BIP32Path("99h")))
        self.assertTrue(
            BIP32PathTemplate("[0-100,200-300]h").match_path(
                BIP32Path("299h")))
        self.assertFalse(
            BIP32PathTemplate("[0-100,200-300]h").match_path(
                BIP32Path("199h")))
        self.assertTrue(BIP32PathTemplate("m").match_path(BIP32Path("m")))
        self.assertTrue(BIP32PathTemplate("").match_path(BIP32Path("")))
        self.assertFalse(BIP32PathTemplate("m").match_path(BIP32Path("")))
        self.assertFalse(BIP32PathTemplate("").match_path(BIP32Path("m")))

        self.assertTrue(
            BIP32PathTemplate('/'.join(str(v) for v in range(255))).match_path(
                BIP32Path('/'.join(str(v) for v in range(255)))))
Example #16
0
def bip_84_keypath():
    BIP_84_KEYPATH = "84'"
    return BIP32Path(BIP_84_KEYPATH)
Example #17
0
def external_chain_keypath():
    EXTERNAL_CHAIN_KEYPATH = "0"
    return BIP32Path(EXTERNAL_CHAIN_KEYPATH)
Example #18
0
    def test_path_from_list(self) -> None:
        with self.assertRaisesRegex(ValueError, 'cannot be negative'):
            BIP32Path([-1])
        with self.assertRaisesRegex(ValueError, 'derivation index cannot be'):
            BIP32Path([0xFFFFFFFF + 1])  # more than 32bit
        with self.assertRaisesRegex(ValueError, 'unsupported hardened_marker'):
            # only apostrophe and "h" markers are allowed
            BIP32Path([0xFFFFFFFF, 0, 0x80000000], hardened_marker='b')

        with self.assertRaisesRegex(
                ValueError, 'derivation path longer than 255 elements'):
            # too long path
            BIP32Path([0 for _ in range(256)])

        self.assertEqual(str(BIP32Path([0], is_partial=False)), "m/0")
        self.assertEqual(str(BIP32Path(BIP32Path([0], is_partial=False))),
                         "m/0")
        self.assertEqual(str(BIP32Path(BIP32Path([0], is_partial=True))), "0")
        self.assertEqual(str(BIP32Path([], is_partial=False)), "m")
        self.assertEqual(str(BIP32Path([0])), "0")
        self.assertEqual(str(BIP32Path([])), "")

        self.assertEqual(
            str(BIP32Path([0xFFFFFFFF, 0x80000001, 1, 0x80000002])),
            "2147483647'/1'/1/2'")

        self.assertEqual(
            str(
                BIP32Path([0xFFFFFFFF, 0x80000001, 1, 2],
                          hardened_marker='h',
                          is_partial=False)), "m/2147483647h/1h/1/2")

        self.assertEqual(
            str(
                BIP32Path([n + BIP32_HARDENED_KEY_OFFSET
                           for n in range(128)] + [n for n in range(127)],
                          is_partial=False)),
            'm/' + '/'.join("%u'" % n for n in range(128)) + '/' +
            '/'.join("%u" % n for n in range(127)))
Example #19
0
def change_chain_keypath():
    CHANGE_CHAIN_KEYPATH = "1"
    return BIP32Path(CHANGE_CHAIN_KEYPATH)
Example #20
0
def bip_44_keypath():
    BIP_44_KEYPATH = "44'"
    return BIP32Path(BIP_44_KEYPATH)
Example #21
0
def bip_49_keypath():
    BIP_49_KEYPATH = "49'"
    return BIP32Path(BIP_49_KEYPATH)
Example #22
0
    def test_from_string(self):
        with self.assertRaises(ValueError):
            BIP32Path('')  # empty path that is not 'm'
        with self.assertRaises(ValueError):
            BIP32Path('m/')  # empty path that is not 'm'
        with self.assertRaises(ValueError):
            BIP32Path('/')
        with self.assertRaises(ValueError):
            BIP32Path('nonsense')
        with self.assertRaises(ValueError):
            BIP32Path('m/-1')
        with self.assertRaises(ValueError):
            BIP32Path('m/4/')  # slash at the end of the path
        with self.assertRaises(ValueError):
            BIP32Path("m/4h/1'")  # inconsistent use of markers
        with self.assertRaises(ValueError):
            BIP32Path("m/2147483648'/1'")  # hardened index too big
        with self.assertRaises(ValueError):
            BIP32Path("m/2147483648/1")  # non-hardened index too big
        with self.assertRaises(ValueError):
            # wrong markers
            BIP32Path("m/2147483647'/1'/0", hardened_marker='h')
        with self.assertRaises(ValueError):
            # wrong markers
            BIP32Path("m/2147483647h/1h/0", hardened_marker="'")
        with self.assertRaises(ValueError):
            # invalid marker
            BIP32Path("m/2147483647h/1h/0", hardened_marker="?")
        with self.assertRaises(ValueError):
            # too long path
            BIP32Path('m/' + '/'.join('0' for _ in range(256)))

        self.assertEqual(list(BIP32Path('m/0')), [0])
        self.assertEqual(list(BIP32Path('m')), [])

        self.assertEqual(
            list(BIP32Path("m/4h/5/1h")),
            [4 + BIP32_HARDENED_KEY_OFFSET, 5, 1 + BIP32_HARDENED_KEY_OFFSET])

        # check that markers correctly picked up from the string
        self.assertEqual(str(BIP32Path("m/4h/5h/1/4")), "m/4h/5h/1/4")
        self.assertEqual(str(BIP32Path("m/4'/5'/1/4")), "m/4'/5'/1/4")

        self.assertEqual(list(BIP32Path("m/0'/2147483647'/1/10")),
                         [BIP32_HARDENED_KEY_OFFSET, 0xFFFFFFFF, 1, 10])

        self.assertEqual(
            list(
                BIP32Path('m/' + '/'.join("%u'" % n
                                          for n in range(128)) + '/' +
                          '/'.join("%u" % (BIP32_HARDENED_KEY_OFFSET - n - 1)
                                   for n in range(127)))),
            [n + BIP32_HARDENED_KEY_OFFSET for n in range(128)] +
            [BIP32_HARDENED_KEY_OFFSET - n - 1 for n in range(127)])