def test_crack() -> None: parent_xpub = "xpub6BabMgRo8rKHfpAb8waRM5vj2AneD4kDMsJhm7jpBDHSJvrFAjHJHU5hM43YgsuJVUVHWacAcTsgnyRptfMdMP8b28LYfqGocGdKCFjhQMV" child_xprv = "xprv9xkG88dGyiurKbVbPH1kjdYrA8poBBBXa53RKuRGJXyruuoJUDd8e4m6poiz7rV8Z4NoM5AJNcPHN6aj8wRFt5CWvF8VPfQCrDUcLU5tcTm" parent_xprv = crack_prv_key(parent_xpub, child_xprv) assert xpub_from_xprv(parent_xprv) == parent_xpub # same check with XKeyDict parent_xprv = crack_prv_key(BIP32KeyData.b58decode(parent_xpub), BIP32KeyData.b58decode(child_xprv)) assert xpub_from_xprv(parent_xprv) == parent_xpub err_msg = "extended parent key is not a public key: " with pytest.raises(BTClibValueError, match=err_msg): crack_prv_key(parent_xprv, child_xprv) err_msg = "extended child key is not a private key: " with pytest.raises(BTClibValueError, match=err_msg): crack_prv_key(parent_xpub, parent_xpub) child_xpub = xpub_from_xprv(child_xprv) with pytest.raises(BTClibValueError, match="not a parent's child: wrong depths"): crack_prv_key(child_xpub, child_xprv) child0_xprv = derive(parent_xprv, 0) grandchild_xprv = derive(child0_xprv, 0) err_msg = "not a parent's child: wrong parent fingerprint" with pytest.raises(BTClibValueError, match=err_msg): crack_prv_key(child_xpub, grandchild_xprv) hardened_child_xprv = derive(parent_xprv, 0x80000000) with pytest.raises(BTClibValueError, match="hardened child derivation"): crack_prv_key(parent_xpub, hardened_child_xprv)
def test_slip132() -> None: mnemonic = "enough regret erode news field main wild jar erupt bronze velvet ugly" mxprv = bip39.mxprv_from_mnemonic(mnemonic) xprv = slip132.p2pkh_xkey(mxprv) assert ( xprv == "xprv9y7Yxxyy7wn5ktGVzAmgatW1vu7daX4V8ddWMcSbKJyK6TzkBbZDimAMCLoogpf4GEp2ThmBZ476vwe7xVC9sPsNfJcyu5isQixgF95HS31" ) with pytest.raises(BTClibValueError, match="not a public key: "): slip132.address_from_xpub(xprv) xpub = bip32.xpub_from_xprv(xprv) assert ( xpub == "xpub6C6uNUWrxKLNyNLy6CJgx2SkUvx7yynLVrZ79zrCseWHyGKtj8sUGZUq3dw9fqJGETSEeX1iztXAfRvxh6Gk2m7yVjDCx5cbRP2So559Hb5" ) address = slip132.address_from_xpub(xpub) assert slip132.address_from_xkey(xprv) == address assert address == "1DjfiAgNyvRXhYXiDgE9K7bfB82hVUPTm1" with pytest.raises(BTClibValueError, match="not a root key: "): slip132.p2pkh_xkey(xprv) yprv = slip132.p2wpkh_p2sh_xkey(mxprv) assert ( yprv == "yprvAJstKLzg5g8RzxfPiX4UT5vH6y5FJkK2yVNkxrzG92WagSCu2PnAxT34JPGCT9Wh5LJCXZi1wB7fd6FUA9veMf2kig7A6cTxD4GMjSbcrqv" ) with pytest.raises(BTClibValueError, match="not a public key: "): slip132.address_from_xpub(yprv) ypub = bip32.xpub_from_xprv(yprv) assert ( ypub == "ypub6XsEirXZv3gjDSjrpYbUpDs1ezujiD2tLiJMmFPshN3ZZEY3Zw6RWFMY9grBtrN88Qan7FAVmtPQnLfuepRy7ZVDNQkkDTMGzYoCJpQdwt4" ) address = slip132.address_from_xpub(ypub) assert slip132.address_from_xkey(yprv) == address assert address == "3QnhAKhuuwSf2bEFKpgvDKUHNe3rpZr6PG" with pytest.raises(BTClibValueError, match="not a root key: "): slip132.p2wpkh_p2sh_xkey(xprv) zprv = slip132.p2wpkh_xkey(mxprv) assert ( zprv == "zprvAceWp8rDBfL7EJ4Mz5p76v6ZmFQ3UyftmmHqfdUidHoTPoFwjApHYCXPhdWNsxLv6ozzauk8LQXNjNn2CcxLaLzezH7QTQ3rk9tn8GmzaXT" ) with pytest.raises(BTClibValueError, match="not a public key: "): slip132.address_from_xpub(zprv) zpub = bip32.xpub_from_xprv(zprv) assert ( zpub == "zpub6qdsDeP722tQSn8q67M7U43JKHEXtSPk8zDSU1tLBdLSGbb6Gi8Y5zqsYuYPhxprjsxc4ZdqvjC86iuv1SXmmvCkJgZJmoCPbDZdHwUaKbX" ) address = slip132.address_from_xpub(zpub) assert slip132.address_from_xkey(zprv) == address assert address == "bc1qcne7y6yae0lz3kceg80aunmafu0rwm3uzmf7v6" with pytest.raises(BTClibValueError, match="not a root key: "): slip132.p2wpkh_xkey(xprv)
def test_p2wpkh_p2sh() -> None: "Test generation of a p2wpkh-p2sh wallet." # https://bitcoinelectrum.com/creating-a-p2sh-segwit-wallet-with-electrum/ # https://www.youtube.com/watch?v=-1DBJWwA2Cw p2wpkh_p2sh_xkey_version = NETWORKS["mainnet"].slip132_p2wpkh_p2sh_prv mnemonics = [ "matrix fitness cook logic peace mercy dinosaur sign measure rescue alert turtle", "chief popular furnace myth decline subject actual toddler plunge rug mixed unlock", ] versions = ["segwit", "standard"] addresses = [ "38Ysa2TRwGAGLEE1pgV2HCX7MAw6XsP6BJ", "3A5u2RTjs3t33Kyc48zHA7Dfsr8Zsfwkoo", ] for mnemonic, version, p2wpkh_p2sh_address in zip(mnemonics, versions, addresses): # this is an electrum mnemonic assert electrum.version_from_mnemonic(mnemonic)[0] == version # of course, it is invalid as BIP39 mnemonic with pytest.raises(BTClibValueError, match="invalid checksum: "): bip39.mxprv_from_mnemonic(mnemonic, "") # nonetheless, let's use it as BIP39 mnemonic rootxprv = bip39.mxprv_from_mnemonic(mnemonic, "", verify_checksum=False) # and force the xkey version to p2wpkh_p2sh mxprv = bip32.derive(rootxprv, "m/49h/0h/0h", p2wpkh_p2sh_xkey_version) mxpub = bip32.xpub_from_xprv(mxprv) # finally, verify the first receiving address xpub = bip32.derive_from_account(mxpub, 0, 0) assert p2wpkh_p2sh_address == slip132.address_from_xkey(xpub)
def test_derive_from_account() -> None: seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad" rmxprv = rootxprv_from_seed(seed) der_path = "m / 44 h / 0 h" mxpub = xpub_from_xprv(derive(rmxprv, der_path)) test_vectors = [ [0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], ] for branch, index in test_vectors: full_path = der_path + f"/{branch}/{index}" addr = p2pkh(derive(rmxprv, full_path)) assert addr == p2pkh(derive_from_account(mxpub, branch, index)) err_msg = "invalid private derivation at branch level" with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 0x80000000, 0, True) err_msg = "too high branch: " with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 0xFFFF + 1, 0) err_msg = "invalid branch: " with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 2, 0) err_msg = "invalid private derivation at address index level" with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 0, 0x80000000) err_msg = "too high address index: " with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 0, 0xFFFF + 1) der_path = "m / 44 h / 0" mxpub = xpub_from_xprv(derive(rmxprv, der_path)) err_msg = "unhardened account/master key" with pytest.raises(BTClibValueError, match=err_msg): derive_from_account(mxpub, 0, 0)
def test_slip132_test_vectors() -> None: """SLIP132 test vector https://github.com/satoshilabs/slips/blob/master/slip-0132.md """ mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" kpath = "m/0/0" test_vectors: List[Tuple[bytes, str, str, str, str]] = [ ( NETWORKS["mainnet"].bip32_prv, "m / 44h / 0h / 0h", "xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb", "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj", "1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA", ), ( NETWORKS["mainnet"].slip132_p2wpkh_p2sh_prv, "m / 49h / 0h / 0h", "yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF", "ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP", "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf", ), ( NETWORKS["mainnet"].slip132_p2wpkh_prv, "m / 84h / 0h / 0h", "zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE", "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", ), ] for version, der_path, prv, pub, addr in test_vectors: rxprv = bip39.mxprv_from_mnemonic(mnemonic, "") mxprv = bip32.derive(rxprv, der_path, version) assert prv == mxprv mxpub = bip32.xpub_from_xprv(mxprv) assert pub == mxpub xpub = bip32.derive(mxpub, kpath) address = slip132.address_from_xpub(xpub) assert addr == address address = slip132.address_from_xkey(xpub) assert addr == address xprv = bip32.derive(mxprv, kpath) address = slip132.address_from_xkey(xprv) assert addr == address if version == NETWORKS["mainnet"].bip32_prv: address = b58.p2pkh(xpub) assert addr == address address = b58.p2pkh(xprv) assert addr == address elif version == NETWORKS["mainnet"].slip132_p2wpkh_p2sh_prv: address = b58.p2wpkh_p2sh(xpub) assert addr == address address = b58.p2wpkh_p2sh(xprv) assert addr == address elif version == NETWORKS["mainnet"].slip132_p2wpkh_prv: address = b32.p2wpkh(xpub) assert addr == address address = b32.p2wpkh(xprv) assert addr == address
def test_serialization() -> None: xkey = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" xkey_data = BIP32KeyData.b58decode(xkey) decoded_key = base58.b58decode(xkey, 78) assert xkey_data.version == decoded_key[:4] assert xkey_data.depth == decoded_key[4] assert xkey_data.parent_fingerprint == decoded_key[5:9] assert xkey_data.index == int.from_bytes(decoded_key[9:13], "big", signed=False) assert xkey_data.chain_code == decoded_key[13:45] assert xkey_data.key == decoded_key[45:] assert xkey_data.b58encode() == xkey xpub = xpub_from_xprv(xkey) xpub2 = xpub_from_xprv(xkey_data) assert xpub == xpub2
def test_derive_exceptions() -> None: # root key, zero depth rootmxprv = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" xprv = BIP32KeyData.b58decode(rootmxprv) # FIXME # assert xprv == _derive(xprv, "m") assert rootmxprv == derive(xprv, "m") assert rootmxprv == derive(xprv, "") fingerprint = hashes.hash160(pub_keyinfo_from_key(xprv)[0])[:4] assert fingerprint == _derive(xprv, bytes.fromhex("80000000")).parent_fingerprint for der_path in ("/1", "800000", "80000000"): xkey = _derive(xprv, der_path) assert fingerprint == xkey.parent_fingerprint err_msg = "invalid literal for int" for der_path in (";/0", "invalid index"): with pytest.raises(ValueError, match=err_msg): derive(xprv, der_path) with pytest.raises(BTClibValueError, match="depth greater than 255: "): derive(xprv, "m" + 256 * "/0") with pytest.raises(BTClibValueError, match="index are not a multiple of 4-bytes: "): derive(xprv, b"\x00" * 5) for index in (2**32, 0x8000000000): with pytest.raises(OverflowError, match="int too big to convert"): derive(xprv, index) xprv = _derive(xprv, "1") err_msg = "final depth greater than 255: " with pytest.raises(BTClibValueError, match=err_msg): derive(xprv, "m" + 255 * "/0") rootxprv = "xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS" temp = base58.b58decode(rootxprv) bad_xprv = base58.b58encode(temp[:45] + b"\x02" + temp[46:], 78) err_msg = "invalid private key prefix: " with pytest.raises(BTClibValueError, match=err_msg): derive(bad_xprv, 0x80000000) xpub = xpub_from_xprv(rootxprv) temp = base58.b58decode(xpub) bad_xpub = base58.b58encode(temp[:45] + b"\x00" + temp[46:], 78) err_msg = r"invalid public key prefix not in \(0x02, 0x03\): " with pytest.raises(BTClibValueError, match=err_msg): derive(bad_xpub, 0x80000000) err_msg = "hardened derivation from public key" with pytest.raises(BTClibValueError, match=err_msg): derive(xpub, 0x80000000)
def test_bips_pr905() -> None: "https://github.com/bitcoin/bips/pull/905" seed = "57fb1e450b8afb95c62afbcd49e4100d6790e0822b8905608679180ac34ca0bd45bf7ccc6c5f5218236d0eb93afc78bd117b9f02a6b7df258ea182dfaef5aad7" xroot = rootxprv_from_seed(seed) der_path = "m/44H/60H/0H" xprv = "xprv9yqXG1Cns3YEQi6fsCJ7NGV5sHPiyZcbgLVst61dbLYyn7qy1G9aFtRmaYp481ounqnVf9Go2ymQ4gmxZLEwYSRhU868aDk4ZxzGvqHJVhe" assert derive(xroot, der_path) == xprv xpub = "xpub6CpsfWjghR6XdCB8yDq7jQRpRKEDP2LT3ZRUgURF9g5xevB7YoTpogkFRqq5nQtVSN8YCMZo2CD8u4zCaxRv85ctCWmzEi9gQ5DBhBFaTNo" assert xpub_from_xprv(xprv) == xpub
def test_slip132() -> None: # xkey is not a public one xprv = b"xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS" err_msg = "not a public key: " with pytest.raises(BTClibValueError, match=err_msg): slip132.address_from_xpub(xprv) address = slip132.address_from_xkey(xprv) xpub = bip32.xpub_from_xprv(xprv) address2 = slip132.address_from_xpub(xpub) assert address == address2
def test_exceptions() -> None: with pytest.raises(BTClibValueError, match="not a private or public key: "): # invalid checksum xprv = "xppp9s21ZrQH143K2oxHiQ5f7D7WYgXD9h6HAXDBuMoozDGGiYHWsq7TLBj2yvGuHTLSPCaFmUyN1v3fJRiY2A4YuNSrqQMPVLZKt76goL6LP7L" p2pkh(xprv) with pytest.raises(BTClibValueError, match="not a private key: "): xpub = "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy" xpub_from_xprv(xpub) seed = "5b56c417303faa3fcba7e57400e120a0" with pytest.raises(BTClibValueError, match="unknown extended key version: "): version = b"\x04\x88\xAD\xE5" rootxprv_from_seed(seed, version) with pytest.raises(BTClibValueError, match="too many bits for seed: "): rootxprv_from_seed(seed * 5) with pytest.raises(BTClibValueError, match="too few bits for seed: "): rootxprv_from_seed(seed[:-2])
def address_from_xkey(xkey: BIP32Key) -> str: """Return the SLIP132 base58/bech32 address. The address is always derived from the compressed public key, as this is the default public key representation in BIP32. """ try: xkey = xpub_from_xprv(xkey) except BTClibValueError: pass return address_from_xpub(xkey)
def test_bip32_vectors() -> None: """BIP32 test vectors https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki """ filename = path.join(data_folder, "bip32_test_vectors.json") with open(filename, "r") as file_: test_vectors = json.load(file_) for seed in test_vectors: mxprv = rootxprv_from_seed(seed) for der_path, xpub, xprv in test_vectors[seed]: assert xprv == derive(mxprv, der_path) assert xpub == xpub_from_xprv(xprv)
def test_p2pkh_from_wif() -> None: seed = b"\x00" * 32 # better be a documented test case rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = b58.wif_from_prv_key(xprv) assert wif == "L2L1dqRmkmVtwStNf5wg8nnGaRn3buoQr721XShM4VwDbTcn9bpm" pub_key, _ = pub_keyinfo_from_prv_key(wif) address = b58.p2pkh(pub_key) xpub = bip32.xpub_from_xprv(xprv) assert address == slip132.address_from_xpub(xpub) err_msg = "not a private key: " with pytest.raises(BTClibValueError, match=err_msg): b58.wif_from_prv_key(xpub)
def test_vectors() -> None: fname = "electrum_test_vectors.json" filename = path.join(path.dirname(__file__), "_data", fname) with open(filename, "r", encoding="ascii") as file_: electrum_test_vectors = json.load(file_) lang = "en" for mnemonic, passphrase, rmxprv, rmxpub, address in electrum_test_vectors: if mnemonic != "": assert rmxprv == electrum.mxprv_from_mnemonic(mnemonic, passphrase) mnemonic_type, mnemonic = electrum.version_from_mnemonic(mnemonic) entr = int(electrum.entropy_from_mnemonic(mnemonic, lang), 2) mnem = electrum.mnemonic_from_entropy(mnemonic_type, entr, lang) assert mnem == mnemonic assert rmxpub == bip32.xpub_from_xprv(rmxprv) xprv = bip32.derive(rmxprv, "m/0h/0") assert address == slip132.address_from_xkey(xprv)
def test_addresses() -> None: # data cross-checked with Electrum and # https://jlopp.github.io/xpub-converter/ # 128 bits raw_entr = bytes.fromhex("6" * 32) # 12 words mnemonic = bip39.mnemonic_from_entropy(raw_entr, "en") # m / purpose h / coin_type h / account h / change / address_index test_vectors: List[Tuple[str, str, str]] = [ # coin_type = 0 -> mainnet ( "m/44h/0h/0h", "bip32_prv", # p2pkh or p2sh "xpub6C3uWu5Go5q62JzJpbjyCLYRGLYvexFeiepZTsYZ6SRexARkNfjG7GKtQVuGR3KHsyKsAwv7Hz3iNucPp6pfHiLvBczyK1j5CtBtpHB3NKx", ), ( "m/49h/0h/0h", "slip132_p2wpkh_p2sh_prv", # p2wpkh-p2sh (i.e., p2sh-wrapped p2wpkh) "ypub6YBGdYufCVeoPVmNXfdrWhaBCXsQoLKNetNmD9bPTrKmnKVmiyU8f1uJqwGdmBb8kbAZpHoYfXQTLbWpkXc4skQDAreeCUXdbX9k8vtiHsN", ), ( "m/49h/0h/0h", "slip132_p2wsh_p2sh_prv", # p2wsh-p2sh (i.e., p2sh-wrapped p2wsh) "Ypub6j5Mkne6mTDAp4vkUL6qLmuyvKug1gzxyA2S8QrvqdABQW4gVNrQk8mEeeE7Kcp2z4EYgsofYjnxTm8b3km22EWt1Km3bszdVFRcipc6rXu", ), ( "m/84h/0h/0h", "slip132_p2wpkh_prv", # p2wpkh "zpub6qg3Uc1BAQkQvcBUYMmZHSzbsshSon3FvJ8yvH3ZZMjFNvJkwSji8UUwghiF3wvpvSvcNWVP8kfUhc2V2RwGp6pTC3ouj6njj956f26TniN", ), ( "m/84h/0h/0h", "slip132_p2wsh_prv", # p2wsh "Zpub72a8bqjcjNJnMBLrV2EY7XLQbfji28irEZneqYK6w8Zf16sfhr7zDbLsVQficP9j9uzbF6VW1y3ypmeFKf6Dxaw82WvK8WFjcsLyEvMNZjF", ), # coin_type = 1 -> testnet ( "m/44h/1h/0h", "bip32_prv", # p2pkh or p2sh "tpubDChqWo2Xi2wNsxyJBE8ipcTJHLKWcqeeNUKBVTpUCNPZkHzHTm3qKAeHqgCou1t8PAY5ZnJ9QDa6zXSZxmjDnhiBpgZ7f6Yv88wEm5HXVbm", ), ( "m/49h/1h/0h", "slip132_p2wpkh_p2sh_prv", # p2wpkh-p2sh (i.e., p2sh-wrapped p2wpkh) "upub5Dj8j7YrwodV68mt58QmNpSzjqjso2WMXEpLGLSvskKccGuXhCh3dTedkzVLAePA617UyXAg2vdswJXTYjU4qjMJaHU79GJVVJCAiy9ezZ2", ), ( "m/49h/1h/0h", "slip132_p2wsh_p2sh_prv", # p2wsh-p2sh (i.e., p2sh-wrapped p2wsh) "Upub5QdDrMHJWmBrWhwG1nskCtnoTdn91PBwqWU1BbiUFXA2ETUSTc5KiaWZZhSoj5c4KUBTr7Anv92P4U9Dqxd1zDTyQkaWYfmVP2U3Js1W5cG", ), ( "m/84h/1h/0h", "slip132_p2wpkh_prv", # p2wpkh "vpub5ZhJmduYY7M5J2qCJgSW7hunX6zJrr5WuNg2kKt321HseZEYxqJc6Zso47aNXQw3Wf3sA8kppbfsxnLheUNXcL3xhzeBHLNp8fTVBN6DnJF", ), ( "m/84h/1h/0h", "slip132_p2wsh_prv", # p2wsh "Vpub5kbPtsdz74uSibzaFLuUwnFbEu2a5Cm7DeKhfb9aPn8HGjoTjEgtBgjirpXr5r9wk87r2ikwhp4P5wxTwhXUkpAdYTkagjqp2PjMmGPBESU", ), ] for der_path, addr_type, mxpub in test_vectors: der_path_elements = der_path.split("/") network = "testnet" if der_path_elements[2] == "1h" else "mainnet" rootprv = bip39.mxprv_from_mnemonic(mnemonic, "", network) version = getattr(NETWORKS[network], addr_type) xprv = bip32.derive(rootprv, der_path, version) assert mxpub == bip32.xpub_from_xprv(xprv) err_msg = "invalid version forced on the extended key" # a non-private version cannot be forced on a private key pub_version = NETWORKS[network].bip32_pub with pytest.raises(BTClibValueError, match=err_msg): bip32.derive(rootprv, der_path, pub_version) # just changing the public version with no derivation does work bip32.derive(mxpub, "m", pub_version) with pytest.raises(BTClibValueError, match=err_msg): bip32.derive(mxpub, "m", version)