import unittest from unittest import mock import lib.bitcoin as bitcoin import lib.keystore as keystore import lib.storage as storage import lib.wallet as wallet from lib import constants from plugins.trustedcoin import trustedcoin from . import TestCaseForTestnet class WalletIntegrityHelper: gap_limit = 1 # make tests run faster @classmethod def check_seeded_keystore_sanity(cls, test_obj, ks): test_obj.assertTrue(ks.is_deterministic()) test_obj.assertFalse(ks.is_watching_only()) test_obj.assertFalse(ks.can_import()) test_obj.assertTrue(ks.has_seed()) @classmethod def check_xpub_keystore_sanity(cls, test_obj, ks): test_obj.assertTrue(ks.is_deterministic()) test_obj.assertTrue(ks.is_watching_only()) test_obj.assertFalse(ks.can_import()) test_obj.assertFalse(ks.has_seed()) @classmethod def create_standard_wallet(cls, ks): store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') store.put('keystore', ks.dump()) store.put('gap_limit', cls.gap_limit) w = wallet.Standard_Wallet(store) w.synchronize() return w @classmethod def create_multisig_wallet(cls, ks1, ks2, ks3=None): """Creates a 2-of-2 or 2-of-3 multisig wallet.""" store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') store.put('x%d/' % 1, ks1.dump()) store.put('x%d/' % 2, ks2.dump()) if ks3 is None: multisig_type = "%dof%d" % (2, 2) else: multisig_type = "%dof%d" % (2, 3) store.put('x%d/' % 3, ks3.dump()) store.put('wallet_type', multisig_type) store.put('gap_limit', cls.gap_limit) w = wallet.Multisig_Wallet(store) w.synchronize() return w # TODO passphrase/seed_extension class TestWalletKeystoreAddressIntegrityForMainnet(unittest.TestCase): @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_seed_standard(self, mock_write): seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song' self.assertEqual(bitcoin.seed_type(seed_words), 'standard') ks = keystore.from_seed(seed_words, '', False) WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks) self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K32jECVM729vWgGq4mUDJCk1ozqAStTphzQtCTuoFmFafNoG1g55iCnBTXUzz3zWnDb5CVLGiFvmaZjuazHDL8a81cPQ8KL6') self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf') self.assertEqual(w.get_change_addresses()[0], '1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D') @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_seed_segwit(self, mock_write): seed_words = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' self.assertEqual(bitcoin.seed_type(seed_words), 'segwit') ks = keystore.from_seed(seed_words, '', False) WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks) self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xprv, 'zprvAZswDvNeJeha8qZ8g7efN3FXYVJLaEUsE9TW6qXDEbVe74AZ75c2sZFZXPNFzxnhChDQ89oC8C5AjWwHmH1HeRKE1c4kKBQAmjUDdKDUZw2') self.assertEqual(ks.xpub, 'zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2wpkh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af') self.assertEqual(w.get_change_addresses()[0], 'bc1qdy94n2q5qcp0kg7v9yzwe6wvfkhnvyzje7nx2p') @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_seed_old(self, mock_write): seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over' self.assertEqual(bitcoin.seed_type(seed_words), 'old') ks = keystore.from_seed(seed_words, '', False) WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks) self.assertTrue(isinstance(ks, keystore.Old_KeyStore)) self.assertEqual(ks.mpk, 'e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo') self.assertEqual(w.get_change_addresses()[0], '1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe') @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_seed_2fa(self, mock_write): seed_words = 'kiss live scene rude gate step hip quarter bunker oxygen motor glove' self.assertEqual(bitcoin.seed_type(seed_words), '2fa') xprv1, xpub1, xprv2, xpub2 = trustedcoin.TrustedCoinPlugin.xkeys_from_seed(seed_words, '') ks1 = keystore.from_xprv(xprv1) self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xprv, 'xprv9uraXy9F3HP7i8QDqwNTBiD8Jf4bPD4Epif8cS8qbUbgeidUesyZpKmzfcSeHutsGfFnjgih7kzwTB5UQVRNB5LoXaNc8pFusKYx3KVVvYR') self.assertEqual(ks1.xpub, 'xpub68qvwUg8sewQvcUgwxuTYr9rrgu5nfn6BwajQpYT9p8fXWxdCRHpN86UWruWJAD1ede8Sv8ERrTa22Gyc4SBfm7zFpcyoVWVBKCVwnw6s1J') self.assertEqual(ks1.xpub, xpub1) ks2 = keystore.from_xprv(xprv2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) self.assertEqual(ks2.xprv, 'xprv9uraXy9F3HP7kKSiRAvLV7Nrjj7YzspDys7dvGLLu4tLZT49CEBxPWp88dHhVxvZ69SHrPQMUCWjj4Ka2z9kNvs1HAeEf3extGGeSWqEVqf') self.assertEqual(ks2.xpub, 'xpub68qvwUg8sewQxoXBXCTLrFKbHkx3QLY5M63EiejxTQRKSFPHjmWCwK8byvZMM2wZNYA3SmxXoma3M1zxhGESHZwtB7SwrxRgKXAG8dCD2eS') self.assertEqual(ks2.xpub, xpub2) long_user_id, short_id = trustedcoin.get_user_id( {'x1/': {'xpub': xpub1}, 'x2/': {'xpub': xpub2}}) xpub3 = trustedcoin.make_xpub(trustedcoin.get_signing_xpub(), long_user_id) ks3 = keystore.from_xpub(xpub3) WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks3) self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2, ks3) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35L8XmCDoEBKeaWRjvmZvoZvhp8BXMMMPV') self.assertEqual(w.get_change_addresses()[0], '3PeZEcumRqHSPNN43hd4yskGEBdzXgY8Cy') @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_seed_bip44_standard(self, mock_write): seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks = keystore.from_bip39_seed(seed_words, '', "m/44'/0'/0'") self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xprv, 'xprv9zGLcNEb3cHUKizLVBz6RYeE9bEZAVPjH2pD1DEzCnPcsemWc3d3xTao8sfhfUmDLMq6e3RcEMEvJG1Et8dvfL8DV4h7mwm9J6AJsW9WXQD') self.assertEqual(ks.xpub, 'xpub6DFh1smUsyqmYD4obDX6ngaxhd53Zx7aeFjoobebm7vbkT6f9awJWFuGzBT9FQJEWFBL7UyhMXtYzRcwDuVbcxtv9Ce2W9eMm4KXLdvdbjv') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2pkh') self.assertEqual(w.get_receiving_addresses()[0], '16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo') self.assertEqual(w.get_change_addresses()[0], '1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn') @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_seed_bip49_p2sh_segwit(self, mock_write): seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks = keystore.from_bip39_seed(seed_words, '', "m/49'/0'/0'") self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xprv, 'yprvAJEYHeNEPcyBoQYM7sGCxDiNCTX65u4ANgZuSGTrKN5YCC9MP84SBayrgaMyZV7zvkHrr3HVPTK853s2SPk4EttPazBZBmz6QfDkXeE8Zr7') self.assertEqual(ks.xpub, 'ypub6XDth9u8DzXV1tcpDtoDKMf6kVMaVMn1juVWEesTshcX4zUVvfNgjPJLXrD9N7AdTLnbHFL64KmBn3SNaTe69iZYbYCqLCCNPZKbLz9niQ4') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2wpkh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35ohQTdNykjkF1Mn9nAVEFjupyAtsPAK1W') self.assertEqual(w.get_change_addresses()[0], '3KaBTcviBLEJajTEMstsA2GWjYoPzPK7Y7') @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_seed_bip84_native_segwit(self, mock_write): # test case from bip84 seed_words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks = keystore.from_bip39_seed(seed_words, '', "m/84'/0'/0'") self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xprv, 'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE') self.assertEqual(ks.xpub, 'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs') w = WalletIntegrityHelper.create_standard_wallet(ks) self.assertEqual(w.txin_type, 'p2wpkh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu') self.assertEqual(w.get_change_addresses()[0], 'bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el') @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_multisig_seed_standard(self, mock_write): seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure' self.assertEqual(bitcoin.seed_type(seed_words), 'standard') ks1 = keystore.from_seed(seed_words, '', True) WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks1) self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xprv, 'xprv9s21ZrQH143K3t9vo23J3hajRbzvkRLJ6Y1zFrUFAfU3t8oooMPfb7f87cn5KntgqZs5nipZkCiBFo5ZtaSD2eDo7j7CMuFV8Zu6GYLTpY6') self.assertEqual(ks1.xpub, 'xpub661MyMwAqRbcGNEPu3aJQqXTydqR9t49Tkwb4Esrj112kw8xLthv8uybxvaki4Ygt9xiwZUQGeFTG7T2TUzR3eA4Zp3aq5RXsABHFBUrq4c') # electrum seed: ghost into match ivory badge robot record tackle radar elbow traffic loud ks2 = keystore.from_xpub('xpub661MyMwAqRbcGfCPEkkyo5WmcrhTq8mi3xuBS7VEZ3LYvsgY1cCFDbenT33bdD12axvrmXhuX3xkAbKci3yZY9ZEk8vhLic7KNhLjqdh5ec') WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN') self.assertEqual(w.get_change_addresses()[0], '36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1') @mock.patch.object(storage.WalletStorage, '_write') def test_electrum_multisig_seed_segwit(self, mock_write): seed_words = 'snow nest raise royal more walk demise rotate smooth spirit canyon gun' self.assertEqual(bitcoin.seed_type(seed_words), 'segwit') ks1 = keystore.from_seed(seed_words, '', True) WalletIntegrityHelper.check_seeded_keystore_sanity(self, ks1) self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xprv, 'ZprvAjxLRqPiDfPDxXrm8JvcoCGRAW6xUtktucG6AMtdzaEbTEJN8qcECvujfhtDU3jLJ9g3Dr3Gz5m1ypfMs8iSUh62gWyHZ73bYLRWyeHf6y4') self.assertEqual(ks1.xpub, 'Zpub6xwgqLvc42wXB1wEELTdALD9iXwStMUkGqBgxkJFYumaL2dWgNvUkjEDWyDFZD3fZuDWDzd1KQJ4NwVHS7hs6H6QkpNYSShfNiUZsgMdtNg') # electrum seed: hedgehog sunset update estate number jungle amount piano friend donate upper wool ks2 = keystore.from_xpub('Zpub6y4oYeETXAbzLNg45wcFDGwEG3vpgsyMJybiAfi2pJtNF3i3fJVxK2BeZJaw7VeKZm192QHvXP3uHDNpNmNDbQft9FiMzkKUhNXQafUMYUY') WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2) self.assertEqual(w.txin_type, 'p2wsh') self.assertEqual(w.get_receiving_addresses()[0], 'bc1qvzezdcv6vs5h45ugkavp896e0nde5c5lg5h0fwe2xyfhnpkxq6gq7pnwlc') self.assertEqual(w.get_change_addresses()[0], 'bc1qxqf840dqswcmu7a8v82fj6ej0msx08flvuy6kngr7axstjcaq6us9hrehd') @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_multisig_seed_bip45_standard(self, mock_write): seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks1 = keystore.from_bip39_seed(seed_words, '', "m/45'/0") self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xprv, 'xprv9vyEFyXf7pYVv4eDU3hhuCEAHPHNGuxX73nwtYdpbLcqwJCPwFKknAK8pHWuHHBirCzAPDZ7UJHrYdhLfn1NkGp9rk3rVz2aEqrT93qKRD9') self.assertEqual(ks1.xpub, 'xpub69xafV4YxC6o8Yiga5EiGLAtqR7rgNgNUGiYgw3S9g9pp6XYUne1KxdcfYtxwmA3eBrzMFuYcNQKfqsXCygCo4GxQFHfywxpUbKNfYvGJka') # bip39 seed: tray machine cook badge night page project uncover ritual toward person enact # der: m/45'/0 ks2 = keystore.from_xpub('xpub6B26nSWddbWv7J3qQn9FbwPPQktSBdPQfLfHhRK4375QoZq8fvM8rQey1koGSTxC5xVoMzNMaBETMUmCqmXzjc8HyAbN7LqrvE4ovGRwNGg') WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2) self.assertEqual(w.txin_type, 'p2sh') self.assertEqual(w.get_receiving_addresses()[0], '3JPTQ2nitVxXBJ1yhMeDwH6q417UifE3bN') self.assertEqual(w.get_change_addresses()[0], '3FGyDuxgUDn2pSZe5xAJH1yUwSdhzDMyEE') @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_multisig_seed_p2sh_segwit(self, mock_write): # bip39 seed: pulse mixture jazz invite dune enrich minor weapon mosquito flight fly vapor # der: m/49'/0'/0' # NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh ks1 = keystore.from_xprv('YprvAUXFReVvDjrPerocC3FxVH748sJUTvYjkAhtKop5VnnzVzMEHr1CHrYQKZwfJn1As3X4LYMav6upxd5nDiLb6SCjRZrBH76EFvyQAG4cn79') self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xpub, 'Ypub6hWbqA2p47QgsLt5J4nxrR3ngu8xsPGb7PdV8CDh48KyNngNqPKSqertAqYhQ4umELu1UsZUCYfj9XPA6AdSMZWDZQobwF7EJ8uNrECaZg1') # bip39 seed: slab mixture skin evoke harsh tattoo rare crew sphere extend balcony frost # der: m/49'/0'/0' ks2 = keystore.from_xpub('Ypub6iNDhL4WWq5kFZcdFqHHwX4YTH4rYGp8xbndpRrY7WNZFFRfogSrL7wRTajmVHgR46AT1cqUG1mrcRd7h1WXwBsgX2QvT3zFbBCDiSDLkau') WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2) self.assertEqual(w.txin_type, 'p2wsh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '35LeC45QgCVeRor1tJD6LiDgPbybBXisns') self.assertEqual(w.get_change_addresses()[0], '39RhtDchc6igmx5tyoimhojFL1ZbQBrXa6') class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): @mock.patch.object(storage.WalletStorage, '_write') def test_bip39_multisig_seed_p2sh_segwit_testnet(self, mock_write): # bip39 seed: finish seminar arrange erosion sunny coil insane together pretty lunch lunch rose # der: m/49'/1'/0' # NOTE: there is currently no bip43 standard derivation path for p2wsh-p2sh ks1 = keystore.from_xprv('Uprv9BEixD3As2LK5h6G2SNT3cTqbZpsWYPceKTSuVAm1yuSybxSvQz2MV1o8cHTtctQmj4HAenb3eh5YJv4YRZjv35i8fofVnNbs4Dd2B4i5je') self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual(ks1.xpub, 'Upub5QE5Mia4hPtcJBAj8TuTQkQa9bfMv17U1YP3hsaNaKSRrQHbTxJGuHLGyv3MbKZixuPyjfXGUdbTjE4KwyFcX8YD7PX5ybTDbP11UT8UpZR') # bip39 seed: square page wood spy oil story rebel give milk screen slide shuffle # der: m/49'/1'/0' ks2 = keystore.from_xpub('Upub5QRzUGRJuWJe5MxGzwgQAeyJjzcdGTXkkq77w6EfBkCyf5iWppSaZ4caY2MgWcU9LP4a4uE5apUFN4wLoENoe9tpu26mrUxeGsH84dN3JFh') WalletIntegrityHelper.check_xpub_keystore_sanity(self, ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = WalletIntegrityHelper.create_multisig_wallet(ks1, ks2) self.assertEqual(w.txin_type, 'p2wsh-p2sh') self.assertEqual(w.get_receiving_addresses()[0], '2MzsfTfTGomPRne6TkctMmoDj6LwmVkDrMt') self.assertEqual(w.get_change_addresses()[0], '2NFp9w8tbYYP9Ze2xQpeYBJQjx3gbXymHX7')