#!/usr/bin/env python3 # # mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet # Copyright (C)2013-2024 The MMGen Project # Licensed under the GNU General Public License, Version 3: # https://www.gnu.org/licenses # Public project repositories: # https://github.com/mmgen/mmgen-wallet # https://gitlab.com/mmgen/mmgen-wallet """ test.unit_tests_d.ut_bip_hd: bip_hd unit test for the MMGen suite """ from mmgen.color import gray,pink,blue from mmgen.util import fmt from mmgen.bip_hd import Bip32ExtendedKey,BipHDConfig,BipHDNode,MasterNode,get_chain_params from ..include.common import cfg,vmsg # Source: BIP-32 vectors_bip32 = [ { 'seed': '000102030405060708090a0b0c0d0e0f', "m": { 'xpub': 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8', 'xprv': 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi', }, "m/0'": { 'xpub': 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw', 'xprv': 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7', }, "m/0'/1": { 'xpub': 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ', 'xprv': 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs', }, "m/0'/1/2'": { 'xpub': 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5', 'xprv': 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM', }, "m/0'/1/2'/2": { 'xpub': 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV', 'xprv': 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334', }, "m/0'/1/2'/2/1000000000": { 'xpub': 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy', 'xprv': 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', }, },{ 'seed': 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542', 'm': { 'xpub': 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB', 'xprv': 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U', }, "m/0": { 'xpub': 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH', 'xprv': 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt', }, "m/0/2147483647'": { 'xpub': 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a', 'xprv': 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9', }, "m/0/2147483647'/1": { 'xpub': 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon', 'xprv': 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef', }, "m/0/2147483647'/1/2147483646'": { 'xpub': 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL', 'xprv': 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc', }, "m/0/2147483647'/1/2147483646'/2": { 'xpub': 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt', 'xprv': 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j', }, },{ 'comment': 'These vectors test for the retention of leading zeros. See bitpay/bitcore-lib#47 and iancoleman/bip39#58 for more information.', 'seed': '4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be', 'm': { 'xpub': 'xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13', 'xprv': 'xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6', }, "m/0'": { 'xpub': 'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y', 'xprv': 'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L', }, },{ 'comment': 'These vectors test for the retention of leading zeros. See btcsuite/btcutil#172 for more information.', 'seed': '3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678', "m": { 'xpub': 'xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa', 'xprv': 'xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv', }, "m/0'": { 'xpub': 'xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m', 'xprv': 'xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G', }, "m/0'/1'": { 'xpub': 'xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt', 'xprv': 'xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1', }, }] # Source: BIP-32 # These vectors test that invalid extended keys are recognized as invalid. vectors_bip32_invalid = [ ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm', 'pubkey version / prvkey mismatch'), ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH', 'prvkey version / pubkey mismatch'), ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn', 'invalid pubkey prefix 04'), ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ', 'invalid prvkey prefix 04'), ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4', 'invalid pubkey prefix 01'), ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J', 'invalid prvkey prefix 01'), ('xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv', 'zero depth with non-zero parent fingerprint'), ('xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ', 'zero depth with non-zero parent fingerprint'), ('xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN', 'zero depth with non-zero index'), ('xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8', 'zero depth with non-zero index'), ('DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4', 'unknown extended key version'), ('DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9', 'unknown extended key version'), ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx', 'private key 0 not in 1..n-1'), ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G', 'private key n not in 1..n-1'), ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY', 'invalid pubkey 02000000...07'), ('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL', 'invalid checksum'), ] # Source: bip_utils vectors_derive = { 'bech32': { 0: 'bc1qwg77fxw0tkmc3h58tcnnpegxk7mp3h6ly44d3n', 1: 'bc1q6g79y6kwpkufevv2njacvnqnsdxmen68jyvjde', 2: 'bc1qknujpwlxc9e9e6avz50q5k90p552xy8g3qjd8u', } } # Source: bip_utils vectors_addrfmt = { 'pub': { 'compressed': 'xpub6GJknXsmpFcEubJsddGacncHhyY5Bk9zMQKC8pC97vBVAphrchYxuoJsAqfZW2uEMfPr6umSPRrhuaA7zeuExkwuAWiUcKcXjSf437VMLwR', 'segwit': 'ypub6aNved9dRKbMRjLfMbGPoXKgNN1tr86qi8WoBxSVr4mHX9fdzUxJFtEH63QZZxArk4f2fwFZQUQ7FRkNiBarTLu2Y69SRxzn68WysngXPrp', 'bech32': 'zpub6urFg31yVogtpf3Y7aV3CLuxQUsEpZ2asqBx3fzYMuFRTFrMNKxn5B2QXAGMSuwVfEA9KSJr2CUs8vqbmhUCkzVvxysB4p6vybLS2CgQnze', }, 'prv': { 'compressed': 'xprvA3KQP2Lsyt3wh7EQXbjaFefZ9whanHS8zBPbLRnXZaeWJ2Ni5AEiMzzPKbKHsG7Dn6hkhSkG8V4H4XUsjszxU4nd2sMnc5ag9sHLLYBqrr4', 'segwit': 'yprvAMPaF7cjax34DFGCFZjPSPNwpLBQSfNzLubCPa2tHjEJeMLVSwe3i5uoEnUg4etxP3XEqr5ZJinjGkUJrte3xFNZ1jVKbjVaFJVHi4Msekw', 'bech32': 'zprvAgruGXV5fS8bcAy51Yx2qCyDrT2kR6JjWcGMFHavoZiSaTXCpneXXNhvfsMLeyBmjzACqkpxB2KGCMAe85wUrW1dnenu6kVHCk4kXh7XFE6', } } # Source: Asgardex Wallet vectors_multicoin = { 'btc_bech32': 'bc1qwg77fxw0tkmc3h58tcnnpegxk7mp3h6ly44d3n', 'eth': '373731f4d885Fc7Da05498F9f0804a87A14F891b', 'doge': 'DFX88RXpi4S4W24YVvuMgbdUcCAYNeEYGd', 'avax-c': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', 'ltc_bech32': 'ltc1q3uh5ga5cp9kkdfx6a52uymxj9keq4tpzep7er0', 'bch_cashaddr': 'bitcoincash:qpqpcllprftg4s0chdgkpxhxv23wfymq3gj7n0a9vw', 'bsc_smart': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', 'bnb_beacon': 'bnb179c3ymltqm4utlp089zxqeta5dvn48a305rhe5', } def wif2addr(cfg,wif): from mmgen.tool.coin import tool_cmd return tool_cmd( cfg = cfg.base_cfg, cmdname = 'wif2addr', proto = cfg.base_cfg._proto, mmtype = cfg.addr_type).wif2addr(wif) class unit_tests: altcoin_deps = ('multicoin',) @property def _seed(self): if not hasattr(self,'__seed'): with open('test/ref/98831F3A.bip39') as fh: mnemonic = fh.read().strip() from mmgen.bip39 import bip39 self.__seed = bip39().generate_seed(mnemonic.split()) return self.__seed def chainparams(self,name,ut): for bipnum,idx,chain,addr_cls in ( (44, 0, 'btc', 'P2PKH'), (49, 0, 'btc', 'P2SH'), (84, 0, 'btc', 'P2WPKH'), (44, 60, 'eth', 'Eth'), (44, 61, 'etc', 'Eth'), (44, 2, 'ltc', 'P2PKH'), (44, 3, 'doge', 'P2PKH'), ): res = get_chain_params(bipnum,chain) assert res.idx == idx, res.idx assert res.chain == chain.upper() assert res.addr_cls == addr_cls vmsg(f' {res}') vmsg('') return True def derive(self,name,ut): vmsg('seed: 98831F3A (default derivation)') m = MasterNode(cfg,self._seed) purpose = m.init_cfg(coin='btc',addr_type='bech32').derive_private() vmsg(f' {purpose.address=}') coin_type1 = purpose.derive_private() coin_type2 = m.to_coin_type('btc',addr_type='bech32') assert coin_type1.address == coin_type2.address vmsg(f' {coin_type1.address=}') acct = coin_type2.derive_private(idx=0) chain1 = acct.derive_private(idx=0,hardened=False) chain2 = m.to_chain(idx=0,coin='btc',addr_type='bech32',public=False) assert chain2.address == chain1.address chain3 = m.to_coin_type(coin='btc',addr_type='bech32').to_chain(0,public=True) assert chain3.address == chain1.address vmsg(f' {chain1.address=}') a = BipHDNode.from_extended_key(cfg,'btc',chain2.xpub) b = BipHDNode.from_extended_key(cfg,'btc',chain2.xprv) vmsg( '\n xpub:\n' + fmt(str(Bip32ExtendedKey(b.xpub)),indent=' ') ) assert a.xpub == b.xpub vmsg(' Addresses:') for i in range(3): res = chain1.derive_public(i) vmsg(f' {i} {res.address}') assert res.address == vectors_derive['bech32'][i] res = chain1.derive_private(i) assert res.address == vectors_derive['bech32'][i] vmsg('') return True def derive_addrfmt(self,name,ut): vmsg('seed: 98831F3A (default derivation)') m = MasterNode(cfg,self._seed) for addr_type in ('compressed','segwit','bech32'): chk_xpub = vectors_addrfmt['pub'][addr_type] chk_xprv = vectors_addrfmt['prv'][addr_type] res1 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_public(0) vmsg(f' {addr_type}: {res1.xpub}') assert res1.xpub == chk_xpub res2 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_private(0,False) vmsg(f' {addr_type}: {res2.xprv}') assert res2.xprv == chk_xprv assert res2.xpub == chk_xpub assert res2.address == wif2addr(res2.cfg, res2.privkey.wif) vmsg('') return True def path(self,name,ut): for vec in vectors_bip32: seed = bytes.fromhex(vec['seed']) vmsg(f'Seed: {vec["seed"]}') for n,path_str in enumerate(vec): if path_str in ('seed','comment'): continue path_arg = path_str.replace("'",'H') if n % 2 else path_str node = BipHDNode.from_path(cfg,seed,path_arg,no_path_checks=True) vmsg(' Path {} {}'.format(pink(path_str),blue('('+node.desc+')'))) for xkey_type in ('xpub','xprv'): vmsg(f' {getattr(node,xkey_type)}') assert getattr(node,xkey_type) == vec[path_str][xkey_type] vmsg('') return True def parse_extended(self,name,ut): vmsg('Parsing and validating extended keys:\n') for vec in vectors_bip32: seed = bytes.fromhex(vec['seed']) vmsg(f' Seed: {vec["seed"]}') for n,path_str in enumerate(vec): if path_str in ('seed','comment'): continue vmsg(' Path {}'.format(pink(path_str))) for xkey_type in ('xpub','xprv'): xkey = vec[path_str][xkey_type] vmsg(f' {xkey}') node = BipHDNode.from_extended_key(cfg,'btc',xkey) assert getattr(node,xkey_type) == xkey vmsg('') return True def multicoin(self,name,ut): m = MasterNode(cfg,self._seed) fs = ' {:6} {:10} {}' vmsg(fs.format('COIN','ADDR_TYPE','ADDR')) for id_str,addr_chk in vectors_multicoin.items(): ss = id_str.split('_') coin = ss[0] addr_type = ss[1] if len(ss) == 2 else None if coin not in BipHDConfig.supported_coins: vmsg(gray(fs.format(coin.upper(), (addr_type or ''), '[not supported yet]'))) continue node = m.to_chain(idx=0,coin=coin,addr_type=addr_type).derive_private(0) xpub_parsed = node.key_extended(public=True) xprv_parsed = node.key_extended(public=False) addr = node.address at_arg = 'compressed' if coin == 'doge' else None from_xpub = BipHDNode.from_extended_key(node.cfg.base_cfg, coin, xpub_parsed.base58, addr_type=at_arg) from_xprv = BipHDNode.from_extended_key(node.cfg.base_cfg, coin, xprv_parsed.base58, addr_type=at_arg) assert from_xpub.xpub == node.xpub, f'{from_xpub.xpub=} != {node.xpub}' assert from_xprv.xpub == node.xpub, f'{from_xprv.xpub=} != {node.xpub}' assert from_xpub.address == addr, f'{from_xpub.address} != {addr}' assert from_xprv.address == addr, f'{from_xprv.address} != {addr}' addr_from_wif = wif2addr(node.cfg, node.privkey.wif) proto = node.cfg.base_cfg._proto if proto.base_proto == 'Ethereum': addr = proto.checksummed_addr(node.address) addr_from_wif = proto.checksummed_addr(addr_from_wif) vmsg(fs.format(coin.upper(), (addr_type or 'auto'), addr)) assert addr == addr_chk, f'{addr} != {addr_chk}' assert addr == addr_from_wif, f'{addr} != {addr_from_wif}' vmsg('') return True def errors(self,name,ut): vmsg('Checking error handling:') m = MasterNode(cfg,self._seed) m_btc = m.init_cfg(coin='btc',addr_type='bech32') purpose = m_btc.derive_private() coin_type = purpose.derive_private() acct = coin_type.derive_private(idx=0) chain = acct.derive_private(idx=0,hardened=False) def bad01(): m.to_chain(idx=0,coin='erq',addr_type='C') def bad02(): m_btc.derive_private(idx=0) def bad03(): m_btc.derive_private(hardened=False) def bad04(): purpose.derive_private(idx=8) def bad05(): purpose.derive_private(hardened=False) def bad06(): coin_type.derive_private() # no acct idx def bad08(): m_btc.derive_public() # must be private def bad09(): coin_type.derive_private(idx=8,hardened=False) def bad10(): acct.derive_private() def bad11(): chain.derive_private() def bad12(): chain.derive_private(hardened=True,idx=3) bad_data = ( ('unsupported coin', 'ValueError', 'not supported', bad01), ('depth 1 (purpose): idx not None', 'ValueError', 'index for path comp', bad02), ('depth 1 (purpose): hardened False', 'ValueError', 'value for ‘hardened’', bad03), ('depth 2 (coin type): idx mismatch', 'ValueError', 'index 8 at depth', bad04), ('depth 2 (coin type): hardened False', 'ValueError', 'value for ‘hardened’', bad05), ('depth 3 (account): idx not set', 'ValueError', 'must be set', bad06), ('depth 1 (purpose): node not hardened', 'ValueError', 'must be hardened', bad08), ('depth 3 (account): node not hardened', 'ValueError', 'value for ‘hardened’', bad09), ('depth 4 (chain): idx not set', 'ValueError', 'must be either 0', bad10), ('depth 5 (leaf node): idx not set', 'ValueError', 'must be set', bad11), ('depth 5 (leaf node): hardened True', 'ValueError', 'must be None', bad12), ) ut.process_bad_data(bad_data,pfx='') vmsg('') return True def parse_extended_errors(self,name,ut): vmsg('Parsing and validating invalid extended keys:') vec = vectors_bip32_invalid func = [lambda m=n: BipHDNode.from_extended_key(cfg,'btc',vec[m][0]) for n in range(len(vec))] exc = ( 'first byte for public', 'first byte for private', 'first byte for public', 'first byte for private', 'first byte for public', 'first byte for private', 'non-zero parent fingerprint', 'non-zero parent fingerprint', 'non-zero index', 'non-zero index', 'unrecognized extended key v', 'unrecognized extended key v', 'private key is zero!', 'private key >= group order!', 'Public key could not be parsed', # extmod 'incorrect checksum', ) ut.process_bad_data([(vec[n][1], 'ValueError', exc[n], func[n]) for n in range(len(vec))],pfx='') vmsg('') return True