def test_testnet(self): # bitcoin core derivation style rootxprv = b'tprv8ZgxMBicQKsPe3g3HwF9xxTLiyc5tNyEtjhBBAk29YA3MTQUqULrmg7aj9qTKNfieuu2HryQ6tGVHse9x7ANFGs3f4HgypMc5nSSoxwf7TK' # m/0'/0'/51' addr1 = b'mfXYCCsvWPgeCv8ZYGqcubpNLYy5nYHbbj' indexes = [0x80000000, 0x80000000, 0x80000000 + 51] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes))) self.assertEqual(addr, addr1) path = "m/0'/0'/51'" addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, path))) self.assertEqual(addr, addr1) # m/0'/1'/150' addr2 = b'mfaUnRFxVvf55uD1P3zWXpprN1EJcKcGrb' indexes = [0x80000000, 0x80000000 + 1, 0x80000000 + 150] addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes))) self.assertEqual(addr, addr2) path = "m/0'/1'/150'" addr = bip32.p2pkh_address_from_xpub( bip32.xpub_from_xprv(bip32.derive(rootxprv, path))) self.assertEqual(addr, addr2)
def test_slip132_test_vector() -> 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 = "./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_str in test_vectors: addr = addr_str.encode() rxprv = bip32.mxprv_from_bip39_mnemonic(mnemonic, "") mxprv = bip32.derive(rxprv, der_path, version) assert prv.encode() == mxprv mxpub = bip32.xpub_from_xprv(mxprv) assert pub.encode() == 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 = base58address.p2pkh(xpub) assert addr == address address = base58address.p2pkh(xprv) assert addr == address elif version == NETWORKS["mainnet"]["slip132_p2wpkh_p2sh_prv"]: address = base58address.p2wpkh_p2sh(xpub) assert addr == address address = base58address.p2wpkh_p2sh(xprv) assert addr == address elif version == NETWORKS["mainnet"]["slip132_p2wpkh_prv"]: address = bech32address.p2wpkh(xpub) assert addr == address address = bech32address.p2wpkh(xprv) assert addr == address
def test_crack(self): parent_xpub = b'xpub6BabMgRo8rKHfpAb8waRM5vj2AneD4kDMsJhm7jpBDHSJvrFAjHJHU5hM43YgsuJVUVHWacAcTsgnyRptfMdMP8b28LYfqGocGdKCFjhQMV' child_xprv = b'xprv9xkG88dGyiurKbVbPH1kjdYrA8poBBBXa53RKuRGJXyruuoJUDd8e4m6poiz7rV8Z4NoM5AJNcPHN6aj8wRFt5CWvF8VPfQCrDUcLU5tcTm' parent_xprv = crack_prvkey(parent_xpub, child_xprv) self.assertEqual(xpub_from_xprv(parent_xprv), parent_xpub) # same check with XKeyDict parent_xprv = crack_prvkey(deserialize(parent_xpub), deserialize(child_xprv)) self.assertEqual(xpub_from_xprv(parent_xprv), parent_xpub) # extended parent key is not a public one self.assertRaises(ValueError, crack_prvkey, parent_xprv, child_xprv) #crack_prvkey(parent_xprv, child_xprv) # extended child key is not a private one self.assertRaises(ValueError, crack_prvkey, parent_xpub, parent_xpub) #crack_prvkey(parent_xpub, parent_xpub) # wrong child/parent depth relation child_xpub = xpub_from_xprv(child_xprv) self.assertRaises(ValueError, crack_prvkey, child_xpub, child_xprv) #crack_prvkey(child_xpub, child_xprv) # not a child for the provided parent child0_xprv = derive(parent_xprv, 0) grandchild_xprv = derive(child0_xprv, 0) self.assertRaises(ValueError, crack_prvkey, child_xpub, grandchild_xprv) #crack_prvkey(child_xpub, grandchild_xprv) # hardened derivation hardened_child_xprv = derive(parent_xprv, 0x80000000) self.assertRaises(ValueError, crack_prvkey, parent_xpub, hardened_child_xprv)
def test_crack(): parent_xpub = "xpub6BabMgRo8rKHfpAb8waRM5vj2AneD4kDMsJhm7jpBDHSJvrFAjHJHU5hM43YgsuJVUVHWacAcTsgnyRptfMdMP8b28LYfqGocGdKCFjhQMV" child_xprv = "xprv9xkG88dGyiurKbVbPH1kjdYrA8poBBBXa53RKuRGJXyruuoJUDd8e4m6poiz7rV8Z4NoM5AJNcPHN6aj8wRFt5CWvF8VPfQCrDUcLU5tcTm" parent_xprv = crack_prvkey(parent_xpub, child_xprv) assert xpub_from_xprv(parent_xprv).decode() == parent_xpub # same check with XKeyDict parent_xprv = crack_prvkey(deserialize(parent_xpub), deserialize(child_xprv)) assert xpub_from_xprv(parent_xprv).decode() == parent_xpub errmsg = "extended parent key is not a public key: " with pytest.raises(ValueError, match=errmsg): crack_prvkey(parent_xprv, child_xprv) errmsg = "extended child key is not a private key: " with pytest.raises(ValueError, match=errmsg): crack_prvkey(parent_xpub, parent_xpub) child_xpub = xpub_from_xprv(child_xprv) with pytest.raises(ValueError, match="not a parent's child: wrong depths"): crack_prvkey(child_xpub, child_xprv) child0_xprv = derive(parent_xprv, 0) grandchild_xprv = derive(child0_xprv, 0) errmsg = "not a parent's child: wrong parent fingerprint" with pytest.raises(ValueError, match=errmsg): crack_prvkey(child_xpub, grandchild_xprv) hardened_child_xprv = derive(parent_xprv, 0x80000000) with pytest.raises(ValueError, match="hardened child derivation"): crack_prvkey(parent_xpub, hardened_child_xprv)
def test_slip32_test_vector(self): """SLIP32 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 = "./0/0" test_vectors = [ [ MAIN_xprv, "m / 44h / 0h / 0h", b'xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb', b'xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj', b'1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA' ], [ MAIN_yprv, "m / 49h / 0h / 0h", b'yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF', b'ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP', b'37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf' ], [ MAIN_zprv, "m / 84h / 0h / 0h", b'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE', b'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs', b'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu' ], ] for v in test_vectors: rxprv = bip32.rootxprv_from_bip39mnemonic(mnemonic, '', v[0]) mxprv = bip32.derive(rxprv, v[1]) self.assertEqual(v[2], mxprv) mxpub = bip32.xpub_from_xprv(mxprv) self.assertEqual(v[3], mxpub) xpub = bip32.derive(mxpub, kpath) address = slip32.address_from_xpub(xpub) self.assertEqual(v[4], address) address = slip32.address_from_xkey(xpub) self.assertEqual(v[4], address) xprv = bip32.derive(mxprv, kpath) address = slip32.address_from_xkey(xprv) self.assertEqual(v[4], address) if v[0] == MAIN_xprv: address = base58address.p2pkh(xpub) self.assertEqual(v[4], address) address = base58address.p2pkh(xprv) self.assertEqual(v[4], address) elif v[0] == MAIN_yprv: address = base58address.p2wpkh_p2sh(xpub) self.assertEqual(v[4], address) address = base58address.p2wpkh_p2sh(xprv) self.assertEqual(v[4], address) elif v[0] == MAIN_zprv: address = bech32address.p2wpkh(xpub) self.assertEqual(v[4], address) address = bech32address.p2wpkh(xprv) self.assertEqual(v[4], address)
def test_fingerprint(self): xprv = b"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" f = fingerprint(xprv) child_key = derive(xprv, b'\x00\x00\x00\x00') pf = deserialize(child_key)['parent_fingerprint'] self.assertEqual(f, pf) xpub = xpub_from_xprv(xprv) f = fingerprint(xpub) self.assertEqual(f, pf) child_key2 = derive(deserialize(xprv), 0) self.assertEqual(child_key2, child_key)
def test_fingerprint(): xpub = "xpub661MyMwAqRbcFMYjmw8C6dJV97a4oLss6hb3v9wTQn2X48msQB61RCaLGtNhzgPCWPaJu7SvuB9EBSFCL43kTaFJC3owdaMka85uS154cEh" pf = fingerprint(xpub) child_key = bip32.derive(xpub, 0) pf2 = bip32.deserialize(child_key)["parent_fingerprint"] assert pf == pf2
def test_crack(self): parent_xpub = b'xpub6BabMgRo8rKHfpAb8waRM5vj2AneD4kDMsJhm7jpBDHSJvrFAjHJHU5hM43YgsuJVUVHWacAcTsgnyRptfMdMP8b28LYfqGocGdKCFjhQMV' child_xprv = b'xprv9xkG88dGyiurKbVbPH1kjdYrA8poBBBXa53RKuRGJXyruuoJUDd8e4m6poiz7rV8Z4NoM5AJNcPHN6aj8wRFt5CWvF8VPfQCrDUcLU5tcTm' parent_xprv = bip32.crack(parent_xpub, child_xprv) self.assertEqual(bip32.xpub_from_xprv(parent_xprv), parent_xpub) index = bip32.child_index(child_xprv) self.assertEqual(bip32.ckd(parent_xprv, index), child_xprv) path = [index] self.assertEqual(bip32.derive(parent_xprv, path), child_xprv) # extended parent key is not a public one self.assertRaises(ValueError, bip32.crack, parent_xprv, child_xprv) #bip32.crack(parent_xprv, child_xprv) # extended child key is not a private one self.assertRaises(ValueError, bip32.crack, parent_xpub, parent_xpub) #bip32.crack(parent_xpub, parent_xpub) # wrong child/parent depth relation child_xpub = bip32.xpub_from_xprv(child_xprv) self.assertRaises(ValueError, bip32.crack, child_xpub, child_xprv) #bip32.crack(child_xpub, child_xprv) # not a child for the provided parent child0_xprv = bip32.ckd(parent_xprv, 0) grandchild_xprv = bip32.ckd(child0_xprv, 0) self.assertRaises(ValueError, bip32.crack, child_xpub, grandchild_xprv) #bip32.crack(child_xpub, grandchild_xprv) # hardened derivation hardened_child_xprv = bip32.ckd(parent_xprv, 0x80000000) self.assertRaises(ValueError, bip32.crack, parent_xpub, hardened_child_xprv)
def derive_child_sec_from_xpub(xpub, path): child_xpub = bip32.derive(xpub, path) child_xpub_bytes = base58.decode_check(child_xpub) # assertion about length? child_sec_bytes = child_xpub_bytes[-33:] child_sec_hex = child_sec_bytes.hex() return child_sec_hex
def test_vectors(): fname = "electrum_test_vectors.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_vectors = json.load(f) lang = "en" for test_vector in test_vectors: mnemonic = test_vector[0] passphrase = test_vector[1] mxprv = test_vector[2] mxpub = test_vector[3] address = test_vector[4] # "./0/0" if mnemonic != "": mxprv2 = bip32.mxprv_from_electrum_mnemonic(mnemonic, passphrase) assert mxprv2 == mxprv.encode() eversion, mnemonic = electrum.version_from_mnemonic(mnemonic) entr = int(electrum.entropy_from_mnemonic(mnemonic, lang), 2) mnem = electrum.mnemonic_from_entropy(entr, eversion, lang) assert mnem == mnemonic if mxprv != "": mxpub2 = bip32.xpub_from_xprv(mxprv) assert mxpub2 == mxpub.encode() xpub = bip32.derive(mxpub, "./0/0") address2 = slip32.address_from_xpub(xpub).decode("ascii") assert address2 == address
def test_fingerprint() -> None: seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad" xprv = rootxprv_from_seed(seed) pf = fingerprint(xprv) # xprv is automatically converted to xpub child_key = derive(xprv, 0x80000000) pf2 = BIP32KeyData.deserialize(child_key).parent_fingerprint assert pf == pf2
def test_exceptions2(self): rootxprv = b'xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS' d = deserialize(rootxprv) self.assertEqual(serialize(d), rootxprv) # invalid 34-bytes key length d['key'] += b'\x00' self.assertRaises(ValueError, serialize, d) # serialize(d) # invalid 33-bytes chain_code length d = deserialize(rootxprv) d['chain_code'] += b'\x00' self.assertRaises(ValueError, serialize, d) # serialize(d) # invalid 5-bytes parent_fingerprint length d = deserialize(rootxprv) d['parent_fingerprint'] += b'\x00' self.assertRaises(ValueError, serialize, d) # serialize(d) # invalid 5-bytes index length d = deserialize(rootxprv) d['index'] += b'\x00' self.assertRaises(ValueError, serialize, d) # serialize(d) # invalid depth (256) d = deserialize(rootxprv) d['depth'] = 256 self.assertRaises(ValueError, serialize, d) # serialize(d) # zero depth with non-zero index b'\x00\x00\x00\x01' d = deserialize(rootxprv) d['index'] = b'\x00\x00\x00\x01' self.assertRaises(ValueError, serialize, d) # serialize(d) # zero depth with non-zero parent_fingerprint b'\x00\x00\x00\x01' d = deserialize(rootxprv) d['parent_fingerprint'] = b'\x00\x00\x00\x01' self.assertRaises(ValueError, serialize, d) # serialize(d) # non-zero depth (1) with zero parent_fingerprint b'\x00\x00\x00\x00' xprv = deserialize(derive(rootxprv, 1)) xprv['parent_fingerprint'] = b'\x00\x00\x00\x00' self.assertRaises(ValueError, serialize, xprv) # serialize(xprv) # int too big to convert self.assertRaises(OverflowError, derive, rootxprv, 256**4) # Index must be 4-bytes, not 5 self.assertRaises(ValueError, derive, rootxprv, b'\x00' * 5)
def test_fingerprint(self): xpub = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" pf = fingerprint(xpub) # bytes are used to increase code coverage # dict is used to increase code coverage xpubd = bip32.deserialize(xpub) child_key = bip32.derive(xpubd, b'\x00' * 4) pf2 = bip32.deserialize(child_key)['parent_fingerprint'] self.assertEqual(pf, pf2)
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 = b"xprv9yqXG1Cns3YEQi6fsCJ7NGV5sHPiyZcbgLVst61dbLYyn7qy1G9aFtRmaYp481ounqnVf9Go2ymQ4gmxZLEwYSRhU868aDk4ZxzGvqHJVhe" assert derive(xroot, der_path) == xprv xpub = b"xpub6CpsfWjghR6XdCB8yDq7jQRpRKEDP2LT3ZRUgURF9g5xevB7YoTpogkFRqq5nQtVSN8YCMZo2CD8u4zCaxRv85ctCWmzEi9gQ5DBhBFaTNo" assert xpub_from_xprv(xprv) == xpub
def test_testnet(self): # bitcoin core derivation style rootxprv = "tprv8ZgxMBicQKsPe3g3HwF9xxTLiyc5tNyEtjhBBAk29YA3MTQUqULrmg7aj9qTKNfieuu2HryQ6tGVHse9x7ANFGs3f4HgypMc5nSSoxwf7TK" # m / 0h / 0h / 51h addr1 = b"mfXYCCsvWPgeCv8ZYGqcubpNLYy5nYHbbj" indexes = [0x80000000, 0x80000000, 0x80000000 + 51] addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes))) self.assertEqual(addr, addr1) path = "m/0h/0h/51h" addr = p2pkh(xpub_from_xprv(derive(rootxprv, path))) self.assertEqual(addr, addr1) # m / 0h / 1h / 150h addr2 = b"mfaUnRFxVvf55uD1P3zWXpprN1EJcKcGrb" indexes = [0x80000000, 0x80000000 + 1, 0x80000000 + 150] addr = p2pkh(xpub_from_xprv(derive(rootxprv, indexes))) self.assertEqual(addr, addr2) path = "m/0h/1h/150h" addr = p2pkh(xpub_from_xprv(derive(rootxprv, path))) self.assertEqual(addr, addr2)
def test_derive_from_account() -> None: seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad" rmxprv = bip32.rootxprv_from_seed(seed) der_path = "m / 44 h / 0 h" mxpub = bip32.xpub_from_xprv(bip32.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(bip32.derive(rmxprv, full_path)).decode() assert addr == p2pkh(bip32.derive_from_account(mxpub, branch, index)).decode() errmsg = "invalid private derivation at branch level" with pytest.raises(ValueError, match=errmsg): bip32.derive_from_account(mxpub, 0x80000000, 0, True) errmsg = "invalid branch: " with pytest.raises(ValueError, match=errmsg): bip32.derive_from_account(mxpub, 2, 0) errmsg = "invalid private derivation at address index level" with pytest.raises(ValueError, match=errmsg): bip32.derive_from_account(mxpub, 0, 0x80000000) der_path = "m / 44 h / 0" mxpub = bip32.xpub_from_xprv(bip32.derive(rmxprv, der_path)) errmsg = "public derivation at account level" with pytest.raises(UserWarning, match=errmsg): bip32.derive_from_account(mxpub, 0, 0)
def test_derive() -> None: test_vectors = { "xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS": [ ["m / 0 h / 0 h / 463 h", "1DyfBWxhVLmrJ7keyiHeMbt7N3UdeGU4G5"], ["M / 0H / 0h // 267' / ", "11x2mn59Qy43DjisZWQGRResjyQmgthki"], ], "tprv8ZgxMBicQKsPe3g3HwF9xxTLiyc5tNyEtjhBBAk29YA3MTQUqULrmg7aj9qTKNfieuu2HryQ6tGVHse9x7ANFGs3f4HgypMc5nSSoxwf7TK": [ ["m / 0 h / 0 h / 51 h", "mfXYCCsvWPgeCv8ZYGqcubpNLYy5nYHbbj"], ["m / 0 h / 1 h / 150 h", "mfaUnRFxVvf55uD1P3zWXpprN1EJcKcGrb"], ], } for rootxprv, value in test_vectors.items(): for der_path, address in value: assert address == p2pkh(bip32.derive(rootxprv, der_path)).decode() b_indexes, _ = bip32._indexes_from_path(der_path) indexes = [int.from_bytes(b_index, "big") for b_index in b_indexes] assert address == p2pkh(bip32.derive(rootxprv, indexes)).decode()
def test_utils(self): # root key, zero depth xprv = b"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" xdict = deserialize(xprv) decoded_key = b58decode(xprv, 78) self.assertEqual(xdict["version"], decoded_key[:4]) self.assertEqual(xdict["depth"], decoded_key[4]) self.assertEqual(xdict["parent_fingerprint"], decoded_key[5:9]) self.assertEqual(xdict["index"], decoded_key[9:13]) self.assertEqual(xdict["chain_code"], decoded_key[13:45]) self.assertEqual(xdict["key"], decoded_key[45:]) # zero depth with non-zero parent_fingerprint f2 = b'\x01\x01\x01\x01' invalid_key = b58encode(xprv[:5] + f2 + xprv[9:]) self.assertRaises(ValueError, deserialize, invalid_key) # deserialize(invalid_key) # zero depth with non-zero index i2 = b'\x01\x01\x01\x01' invalid_key = b58encode(xprv[:9] + i2 + xprv[13:]) self.assertRaises(ValueError, deserialize, invalid_key) # deserialize(invalid_key) # non-zero depth (255) with zero parent_fingerprint d2 = b'ff' invalid_key = b58encode(xprv[:4] + d2 + xprv[5:]) self.assertRaises(ValueError, deserialize, invalid_key) # deserialize(invalid_key) child_key = derive(xprv, 0) # Derivation path final depth 256>255 self.assertRaises(ValueError, derive, child_key, "." + 255 * "/0") #derive(child_key, "."+255*"/0") # Empty derivation path self.assertRaises(ValueError, derive, child_key, "") #derive(child_key, "") # Invalid derivation path root: ";" self.assertRaises(ValueError, derive, child_key, ";/0") #derive(child_key, ";/0") # Derivation path depth 256>255 self.assertRaises(ValueError, derive, child_key, "." + 256 * "/0") #derive(child_key, "." + 256*"/0") # xkey is not a public one self.assertRaises(ValueError, p2pkh_from_xpub, xprv)
def test_vector3(self): """BIP32 test vector 3 https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki """ xkey_version = _PRV_VERSIONS[0] seed = "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be" rootxprv = rootxprv_from_seed(seed, xkey_version) self.assertEqual( rootxprv, b"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6" ) rootxpub = xpub_from_xprv(rootxprv) # neutering self.assertEqual( rootxpub, b"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13" ) xprv = rootxprv xpub = rootxpub xprv = derive(xprv, ".") # private relative self.assertEqual( xprv, b"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6" ) xprv = derive(rootxprv, "m") # private absolute self.assertEqual( xprv, b"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6" ) xpub = derive(xpub, ".") # public relative self.assertEqual( xpub, b"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13" ) xpub = derive(rootxpub, "m") # public absolute self.assertEqual( xpub, b"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13" ) xprv = derive(xprv, "./0'") # private relative self.assertEqual( xprv, b"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L" ) xprv = derive(rootxprv, "m/0'") # private absolute self.assertEqual( xprv, b"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L" ) xpub = xpub_from_xprv(xprv) # neutering self.assertEqual( xpub, b"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y" )
def test_bip32_vectors(): """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 f: test_vectors = json.load(f) 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).decode() assert xpub == xpub_from_xprv(xprv).decode()
def test_p2pkh_from_wif(self): seed = b"00" * 32 # better be random rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = wif_from_xprv(xprv) self.assertEqual( wif, b'KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP') address = p2pkh_from_wif(wif) xpub = bip32.xpub_from_xprv(xprv) address2 = slip32.address_from_xpub(xpub) self.assertEqual(address, address2) self.assertRaises(ValueError, wif_from_xprv, xpub)
def test_bip32_vectors(self): """BIP32 test vector 3 https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki """ fname = "bip32_test_vectors.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_vectors = json.load(f) for seed in test_vectors: mxprv = rootxprv_from_seed(seed) for der_path, xpub, xprv in test_vectors[seed]: self.assertEqual(xprv, derive(mxprv, der_path).decode()) self.assertEqual(xpub, xpub_from_xprv(xprv).decode())
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 = wif_from_prvkey(xprv) assert wif == b"L2L1dqRmkmVtwStNf5wg8nnGaRn3buoQr721XShM4VwDbTcn9bpm" pubkey, _ = pubkeyinfo_from_prvkey(wif) address = p2pkh(pubkey) xpub = bip32.xpub_from_xprv(xprv) address2 = slip132.address_from_xpub(xpub) assert address == address2 err_msg = "not a private key: " with pytest.raises(ValueError, match=err_msg): wif_from_prvkey(xpub)
def derive(mpub: str, start: int, length: int): """ Will generate list of derived addresses, starting at index <n> for <length>. :param mpub: master extended public key :param start: starting index to generate address list :param length: how many addresses to generate :return: a json list of derived addresses """ address_list = {} for index in range(start, start + length): xpub = bip32.derive(xkey=mpub, path=f"./0/{index}") address = slip32.address_from_xpub(xpub).decode("ascii") address_list[f"0/{index}"] = address return json.dumps(address_list)
def test_p2pkh_from_wif() -> None: seed = b"00" * 32 # better be a documented test case rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = wif_from_prvkey(xprv) assert wif == b"KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP" pubkey, _ = pubkeyinfo_from_prvkey(wif) address = p2pkh(pubkey) xpub = bip32.xpub_from_xprv(xprv) address2 = slip132.address_from_xpub(xpub) assert address == address2 err_msg = "not a private key: " with pytest.raises(ValueError, match=err_msg): wif_from_prvkey(xpub)
def test_serialize() -> None: rootxprv = "xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS" d = bip32.deserialize(rootxprv) assert bip32.serialize(d).decode() == rootxprv d["key"] += b"\x00" with pytest.raises(ValueError, match="invalid key length: "): bip32.serialize(d) d = bip32.deserialize(rootxprv) d["depth"] = 256 with pytest.raises(ValueError, match="invalid depth "): bip32.serialize(d) d = bip32.deserialize(rootxprv) d["parent_fingerprint"] = b"\x00\x00\x00\x01" errmsg = "zero depth with non-zero parent fingerprint 0x" with pytest.raises(ValueError, match=errmsg): bip32.serialize(d) d = bip32.deserialize(rootxprv) d["index"] = b"\x00\x00\x00\x01" with pytest.raises(ValueError, match="zero depth with non-zero index 0x"): bip32.serialize(d) xprv = bip32.deserialize(bip32.derive(rootxprv, 0x80000000)) xprv["parent_fingerprint"] = b"\x00\x00\x00\x00" errmsg = "zero parent fingerprint with non-zero depth " with pytest.raises(ValueError, match=errmsg): bip32.serialize(xprv) d = bip32.deserialize(rootxprv) d["parent_fingerprint"] += b"\x00" with pytest.raises(ValueError, match="invalid parent fingerprint length: "): bip32.serialize(d) d = bip32.deserialize(rootxprv) d["index"] += b"\x00" with pytest.raises(ValueError, match="invalid index length: "): bip32.serialize(d) d = bip32.deserialize(rootxprv) d["chain_code"] += b"\x00" with pytest.raises(ValueError, match="invalid chain code length: "): bip32.serialize(d)
def test_slip32(self): """SLIP32 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" passphrase = "" path = "m/44'/0'/0'" prv = b"xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb" pub = b"xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj" address = b"1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA" rxprv = bip32.rootxprv_from_bip39mnemonic(mnemonic, passphrase, MAIN_xprv) mprv = bip32.derive(rxprv, path) self.assertEqual(mprv, prv) mpub = bip32.xpub_from_xprv(mprv) self.assertEqual(mpub, pub) pub = bip32.derive(mpub, "./0/0") addr = slip32.address_from_xpub(pub) self.assertEqual(address, addr) path = "m/49'/0'/0'" prv = b"yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF" pub = b"ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP" address = b"37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf" rxprv = bip32.rootxprv_from_bip39mnemonic(mnemonic, passphrase, MAIN_yprv) mprv = bip32.derive(rxprv, path) self.assertEqual(mprv, prv) mpub = bip32.xpub_from_xprv(mprv) self.assertEqual(mpub, pub) pub = bip32.derive(mpub, "./0/0") addr = slip32.address_from_xpub(pub) self.assertEqual(address, addr) addr = base58address.p2wpkh_p2sh_from_xpub(pub) self.assertEqual(address, addr) path = "m/84'/0'/0'" prv = b"zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE" pub = b"zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs" address = b"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu" rxprv = bip32.rootxprv_from_bip39mnemonic(mnemonic, passphrase, MAIN_zprv) mprv = bip32.derive(rxprv, path) self.assertEqual(mprv, prv) mpub = bip32.xpub_from_xprv(mprv) self.assertEqual(mpub, pub) pub = bip32.derive(mpub, "./0/0") addr = slip32.address_from_xpub(pub) self.assertEqual(address, addr)
def test_vectors(self): filename = "electrum_test_vectors.json" path_to_filename = path.join(path.dirname(__file__), "./data/", filename) with open(path_to_filename, 'r') as f: test_vectors = json.load(f) f.closed lang = "en" for test_vector in test_vectors: mnemonic = test_vector[0] passphrase = test_vector[1] mxprv = test_vector[2] mxpub = test_vector[3] address = test_vector[4] # "./0/0" if mnemonic != "": mxprv2 = electrum.masterxprv_from_mnemonic( mnemonic, passphrase) self.assertEqual(mxprv2.decode(), mxprv) eversion = electrum.version_from_mnemonic(mnemonic) entr = int(electrum.entropy_from_mnemonic(mnemonic, lang), 2) mnem = electrum.mnemonic_from_entropy(eversion, entr, lang) self.assertEqual(mnem, mnemonic) if mxprv != "": mxpub2 = bip32.xpub_from_xprv(mxprv) self.assertEqual(mxpub2.decode(), mxpub) xpub = bip32.derive(mxpub, "./0/0") address2 = bip32.address_from_xpub(xpub).decode() self.assertEqual(address2, address) # version 2fa_segwit mnemonic = "slender flight session office noodle hand couple option office wait uniform morning" self.assertEqual("2fa_segwit", electrum.version_from_mnemonic(mnemonic)) # version 2fa mnemonic = "history recycle company awful donor fold beef nominee hard bleak bracket six" self.assertEqual("2fa", electrum.version_from_mnemonic(mnemonic))
def test_vectors(self): fname = "electrum_test_vectors.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_vectors = json.load(f) lang = "en" for test_vector in test_vectors: mnemonic = test_vector[0] passphrase = test_vector[1] mxprv = test_vector[2] mxpub = test_vector[3] address = test_vector[4] # "./0/0" if mnemonic != "": mxprv2 = bip32.mxprv_from_electrum_mnemonic( mnemonic, passphrase) self.assertEqual(mxprv2, mxprv.encode()) eversion, mnemonic = electrum.version_from_mnemonic(mnemonic) entr = int(electrum.entropy_from_mnemonic(mnemonic, lang), 2) mnem = electrum.mnemonic_from_entropy(entr, eversion, lang) self.assertEqual(mnem, mnemonic) if mxprv != "": mxpub2 = bip32.xpub_from_xprv(mxprv) self.assertEqual(mxpub2, mxpub.encode()) xpub = bip32.derive(mxpub, "./0/0") address2 = slip32.address_from_xpub(xpub).decode("ascii") self.assertEqual(address2, address) # version 2fa_segwit mnemonic = ("slender flight session office noodle hand " "couple option office wait uniform morning") self.assertEqual("2fa_segwit", electrum.version_from_mnemonic(mnemonic)[0]) # version 2fa mnemonic = ("history recycle company awful donor fold " "beef nominee hard bleak bracket six") self.assertEqual("2fa", electrum.version_from_mnemonic(mnemonic)[0])
def test_vectors() -> None: fname = "electrum_test_vectors.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_vectors = json.load(f) lang = "en" for mnemonic, passphrase, rmxprv, rmxpub, address in test_vectors: if mnemonic != "": mxprv2 = bip32.mxprv_from_electrum_mnemonic(mnemonic, passphrase) assert mxprv2 == rmxprv.encode() eversion, mnemonic = electrum.version_from_mnemonic(mnemonic) entr = int(electrum.entropy_from_mnemonic(mnemonic, lang), 2) mnem = electrum.mnemonic_from_entropy(entr, eversion, lang) assert mnem == mnemonic assert rmxpub.encode() == bip32.xpub_from_xprv(rmxprv) xprv = bip32.derive(rmxprv, "m/0h/0") address2 = slip132.address_from_xkey(xprv).decode("ascii") assert address2 == address