You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
15 KiB
282 lines
15 KiB
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 plugins.trustedcoin import trustedcoin |
|
|
|
|
|
# TODO passphrase/seed_extension |
|
class TestWalletKeystoreAddressIntegrity(unittest.TestCase): |
|
|
|
gap_limit = 1 # make tests run faster |
|
|
|
def _check_seeded_keystore_sanity(self, ks): |
|
self.assertTrue (ks.is_deterministic()) |
|
self.assertFalse(ks.is_watching_only()) |
|
self.assertFalse(ks.can_import()) |
|
self.assertTrue (ks.has_seed()) |
|
|
|
def _check_xpub_keystore_sanity(self, ks): |
|
self.assertTrue (ks.is_deterministic()) |
|
self.assertTrue (ks.is_watching_only()) |
|
self.assertFalse(ks.can_import()) |
|
self.assertFalse(ks.has_seed()) |
|
|
|
def _create_standard_wallet(self, ks): |
|
store = storage.WalletStorage('if_this_exists_mocking_failed_648151893') |
|
store.put('keystore', ks.dump()) |
|
store.put('gap_limit', self.gap_limit) |
|
w = wallet.Standard_Wallet(store) |
|
w.synchronize() |
|
return w |
|
|
|
def _create_multisig_wallet(self, 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', self.gap_limit) |
|
w = wallet.Multisig_Wallet(store) |
|
w.synchronize() |
|
return w |
|
|
|
@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) |
|
|
|
self._check_seeded_keystore_sanity(ks) |
|
self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) |
|
|
|
self.assertEqual(ks.xprv, 'xprv9s21ZrQH143K32jECVM729vWgGq4mUDJCk1ozqAStTphzQtCTuoFmFafNoG1g55iCnBTXUzz3zWnDb5CVLGiFvmaZjuazHDL8a81cPQ8KL6') |
|
self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U') |
|
|
|
w = self._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) |
|
|
|
self._check_seeded_keystore_sanity(ks) |
|
self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) |
|
|
|
self.assertEqual(ks.xprv, 'zprvAZswDvNeJeha8qZ8g7efN3FXYVJLaEUsE9TW6qXDEbVe74AZ75c2sZFZXPNFzxnhChDQ89oC8C5AjWwHmH1HeRKE1c4kKBQAmjUDdKDUZw2') |
|
self.assertEqual(ks.xpub, 'zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ') |
|
|
|
w = self._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) |
|
|
|
self._check_seeded_keystore_sanity(ks) |
|
self.assertTrue(isinstance(ks, keystore.Old_KeyStore)) |
|
|
|
self.assertEqual(ks.mpk, 'e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3') |
|
|
|
w = self._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) |
|
self._check_xpub_keystore_sanity(ks3) |
|
self.assertTrue(isinstance(ks3, keystore.BIP32_KeyStore)) |
|
|
|
w = self._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 = self._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 = self._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 = self._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) |
|
self._check_seeded_keystore_sanity(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') |
|
self._check_xpub_keystore_sanity(ks2) |
|
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) |
|
|
|
w = self._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) |
|
self._check_seeded_keystore_sanity(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') |
|
self._check_xpub_keystore_sanity(ks2) |
|
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) |
|
|
|
w = self._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') |
|
self._check_xpub_keystore_sanity(ks2) |
|
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) |
|
|
|
w = self._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') |
|
self._check_xpub_keystore_sanity(ks2) |
|
self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) |
|
|
|
w = self._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')
|
|
|