test_commands.py (13026B)
1 import unittest 2 from unittest import mock 3 from decimal import Decimal 4 5 from electrum.util import create_and_start_event_loop 6 from electrum.commands import Commands, eval_bool 7 from electrum import storage, wallet 8 from electrum.wallet import restore_wallet_from_text 9 from electrum.simple_config import SimpleConfig 10 11 from . import TestCaseForTestnet, ElectrumTestCase 12 13 14 class TestCommands(ElectrumTestCase): 15 16 def setUp(self): 17 super().setUp() 18 self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() 19 self.config = SimpleConfig({'electrum_path': self.electrum_path}) 20 21 def tearDown(self): 22 super().tearDown() 23 self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) 24 self._loop_thread.join(timeout=1) 25 26 def test_setconfig_non_auth_number(self): 27 self.assertEqual(7777, Commands._setconfig_normalize_value('rpcport', "7777")) 28 self.assertEqual(7777, Commands._setconfig_normalize_value('rpcport', '7777')) 29 self.assertAlmostEqual(Decimal(2.3), Commands._setconfig_normalize_value('somekey', '2.3')) 30 31 def test_setconfig_non_auth_number_as_string(self): 32 self.assertEqual("7777", Commands._setconfig_normalize_value('somekey', "'7777'")) 33 34 def test_setconfig_non_auth_boolean(self): 35 self.assertEqual(True, Commands._setconfig_normalize_value('show_console_tab', "true")) 36 self.assertEqual(True, Commands._setconfig_normalize_value('show_console_tab', "True")) 37 38 def test_setconfig_non_auth_list(self): 39 self.assertEqual(['file:///var/www/', 'https://electrum.org'], 40 Commands._setconfig_normalize_value('url_rewrite', "['file:///var/www/','https://electrum.org']")) 41 self.assertEqual(['file:///var/www/', 'https://electrum.org'], 42 Commands._setconfig_normalize_value('url_rewrite', '["file:///var/www/","https://electrum.org"]')) 43 44 def test_setconfig_auth(self): 45 self.assertEqual("7777", Commands._setconfig_normalize_value('rpcuser', "7777")) 46 self.assertEqual("7777", Commands._setconfig_normalize_value('rpcuser', '7777')) 47 self.assertEqual("7777", Commands._setconfig_normalize_value('rpcpassword', '7777')) 48 self.assertEqual("2asd", Commands._setconfig_normalize_value('rpcpassword', '2asd')) 49 self.assertEqual("['file:///var/www/','https://electrum.org']", 50 Commands._setconfig_normalize_value('rpcpassword', "['file:///var/www/','https://electrum.org']")) 51 52 def test_eval_bool(self): 53 self.assertFalse(eval_bool("False")) 54 self.assertFalse(eval_bool("false")) 55 self.assertFalse(eval_bool("0")) 56 self.assertTrue(eval_bool("True")) 57 self.assertTrue(eval_bool("true")) 58 self.assertTrue(eval_bool("1")) 59 60 def test_convert_xkey(self): 61 cmds = Commands(config=self.config) 62 xpubs = { 63 ("xpub6CCWFbvCbqF92kGwm9nV7t7RvVoQUKaq5USMdyVP6jvv1NgN52KAX6NNYCeE8Ca7JQC4K5tZcnQrubQcjJ6iixfPs4pwAQJAQgTt6hBjg11", "standard"), 64 ("ypub6X2mZGb7kWnct3U4bWa7KyCw6TwrQwaKzaxaRNPGUkJo4UVbKgUj9A2WZQbp87E2i3Js4ZV85SmQnt2BSzWjXCLzjQXMkK7egQXXVHT4eKn", "p2wpkh-p2sh"), 65 ("zpub6qs2rwG2uCL6jLfBRsMjY4JSGS6JMZZpuhUoCmH9rkgg7aJpaLeHmDgeacZQ81sx7gRfp35gY77xgAdkAgvkKS2bbkDnLDw8x8bAsuKBrvP", "p2wpkh"), 66 } 67 for xkey1, xtype1 in xpubs: 68 for xkey2, xtype2 in xpubs: 69 self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) 70 71 xprvs = { 72 ("xprv9yD9r6PJmTgqpGCUf8FUkkAhNTxv4rryiFWkqb5mYQPw8aMDXUzuyJ3tgv5vUqYkdK1E6Q5jKxPss4HkMBYV4q8AfG8t7rxgyS4xQX4ndAm", "standard"), 73 ("yprvAJ3R9m4Dv9EKfZPbVV36xqGCYS7N1UrUdN2ycyyevQmpBgASn9AUbMi2i83WUkCg2x82qsgHnckRkLuK4sxVs4omXbqJhmnBFA8bo8ssinK", "p2wpkh-p2sh"), 74 ("zprvAcsgTRj94pmoWraiKqpjAvMhiQFox6qyYUZCQNsYJR9hEmyg2oL3DRNAjL16UerbSbEqbMGrFH6yddWsnaNWfJVNPwXjHgbfWtCFBgDxFkX", "p2wpkh"), 75 } 76 for xkey1, xtype1 in xprvs: 77 for xkey2, xtype2 in xprvs: 78 self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) 79 80 @mock.patch.object(wallet.Abstract_Wallet, 'save_db') 81 def test_encrypt_decrypt(self, mock_save_db): 82 wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN', 83 path='if_this_exists_mocking_failed_648151893', 84 config=self.config)['wallet'] 85 cmds = Commands(config=self.config) 86 cleartext = "asdasd this is the message" 87 pubkey = "021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da" 88 ciphertext = cmds._run('encrypt', (pubkey, cleartext)) 89 self.assertEqual(cleartext, cmds._run('decrypt', (pubkey, ciphertext), wallet=wallet)) 90 91 @mock.patch.object(wallet.Abstract_Wallet, 'save_db') 92 def test_export_private_key_imported(self, mock_save_db): 93 wallet = restore_wallet_from_text('p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', 94 path='if_this_exists_mocking_failed_648151893', 95 config=self.config)['wallet'] 96 cmds = Commands(config=self.config) 97 # single address tests 98 with self.assertRaises(Exception): 99 cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" 100 with self.assertRaises(Exception): 101 cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet 102 self.assertEqual("p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL", 103 cmds._run('getprivatekeys', ("bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw",), wallet=wallet)) 104 # list of addresses tests 105 with self.assertRaises(Exception): 106 cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'asd'], ), wallet=wallet) 107 self.assertEqual(['p2wpkh:L4jkdiXszG26SUYvwwJhzGwg37H2nLhrbip7u6crmgNeJysv5FHL', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'], 108 cmds._run('getprivatekeys', (['bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], ), wallet=wallet)) 109 110 @mock.patch.object(wallet.Abstract_Wallet, 'save_db') 111 def test_export_private_key_deterministic(self, mock_save_db): 112 wallet = restore_wallet_from_text('bitter grass shiver impose acquire brush forget axis eager alone wine silver', 113 gap_limit=2, 114 path='if_this_exists_mocking_failed_648151893', 115 config=self.config)['wallet'] 116 cmds = Commands(config=self.config) 117 # single address tests 118 with self.assertRaises(Exception): 119 cmds._run('getprivatekeys', ("asdasd",), wallet=wallet) # invalid addr, though might raise "not in wallet" 120 with self.assertRaises(Exception): 121 cmds._run('getprivatekeys', ("bc1qgfam82qk7uwh5j2xxmcd8cmklpe0zackyj6r23",), wallet=wallet) # not in wallet 122 self.assertEqual("p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2", 123 cmds._run('getprivatekeys', ("bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af",), wallet=wallet)) 124 # list of addresses tests 125 with self.assertRaises(Exception): 126 cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'asd'],), wallet=wallet) 127 self.assertEqual(['p2wpkh:L15oxP24NMNAXxq5r2aom24pHPtt3Fet8ZutgL155Bad93GSubM2', 'p2wpkh:L4rYY5QpfN6wJEF4SEKDpcGhTPnCe9zcGs6hiSnhpprZqVywFifN'], 128 cmds._run('getprivatekeys', (['bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', 'bc1q9pzjpjq4nqx5ycnywekcmycqz0wjp2nq604y2n'], ), wallet=wallet)) 129 130 131 class TestCommandsTestnet(TestCaseForTestnet): 132 133 def setUp(self): 134 super().setUp() 135 self.asyncio_loop, self._stop_loop, self._loop_thread = create_and_start_event_loop() 136 self.config = SimpleConfig({'electrum_path': self.electrum_path}) 137 138 def tearDown(self): 139 super().tearDown() 140 self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1) 141 self._loop_thread.join(timeout=1) 142 143 def test_convert_xkey(self): 144 cmds = Commands(config=self.config) 145 xpubs = { 146 ("tpubD8p5qNfjczgTGbh9qgNxsbFgyhv8GgfVkmp3L88qtRm5ibUYiDVCrn6WYfnGey5XVVw6Bc5QNQUZW5B4jFQsHjmaenvkFUgWtKtgj5AdPm9", "standard"), 147 ("upub59wfQ8qJTg6ZSuvwtR313Qdp8gP8TSBwTof5dPQ3QVsYp1N9t29Rr9TGF1pj8kAXUg3mKbmrTKasA2qmBJKb1bGUzB6ApDZpVC7LoHhyvBo", "p2wpkh-p2sh"), 148 ("vpub5UmvhoWDcMe3JD84impdFVjKJeXaQ4BSNvBJQnHvnWFRs7BP8gJzUD7QGDnK8epStKAa55NQuywR3KTKtzjbopx5rWnbQ8PJkvAzBtgaGBc", "p2wpkh"), 149 } 150 for xkey1, xtype1 in xpubs: 151 for xkey2, xtype2 in xpubs: 152 self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) 153 154 xprvs = { 155 ("tprv8c83gxdVUcznP8fMx2iNUBbaQgQC7MUbBUDG3c6YU9xgt7Dn5pfcgHUeNZTAvuYmNgVHjyTzYzGWwJr7GvKCm2FkPaaJipyipbfJeB3tdPW", "standard"), 156 ("uprv8vxJzdJQdJYGERrUnPVzgGh5aeYe3yU66ajUpzzRrALZwD31LUqBJM8nPmQkvpCgnKc6VT4Z1ed4pbTfzcjDZFwMFvGjJjoD6Kix2pCwVe7", "p2wpkh-p2sh"), 157 ("vprv9FnaJHyKmz5k5j3bckHctMnakch5zbTb1hFhcPtKEAiSzJrEb8zjvQnvQyNLvircBxiuEvf7UJycht5EiK9EMVcx8Fy9techN3nbRQRFhEv", "p2wpkh"), 158 } 159 for xkey1, xtype1 in xprvs: 160 for xkey2, xtype2 in xprvs: 161 self.assertEqual(xkey2, cmds._run('convert_xkey', (xkey1, xtype2))) 162 163 def test_serialize(self): 164 cmds = Commands(config=self.config) 165 jsontx = { 166 "inputs": [ 167 { 168 "prevout_hash": "9d221a69ca3997cbeaf5624d723e7dc5f829b1023078c177d37bdae95f37c539", 169 "prevout_n": 1, 170 "value_sats": 1000000, 171 "privkey": "p2wpkh:cVDXzzQg6RoCTfiKpe8MBvmm5d5cJc6JLuFApsFDKwWa6F5TVHpD" 172 } 173 ], 174 "outputs": [ 175 { 176 "address": "tb1q4s8z6g5jqzllkgt8a4har94wl8tg0k9m8kv5zd", 177 "value_sats": 990000 178 } 179 ] 180 } 181 self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402206367fb2ddd723985f5f51e0f2435084c0a66f5c26f4403a75d3dd417b71a20450220545dc3637bcb49beedbbdf5063e05cad63be91af4f839886451c30ecd6edf1d20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", 182 cmds._run('serialize', (jsontx,))) 183 184 def test_serialize_custom_nsequence(self): 185 cmds = Commands(config=self.config) 186 jsontx = { 187 "inputs": [ 188 { 189 "prevout_hash": "9d221a69ca3997cbeaf5624d723e7dc5f829b1023078c177d37bdae95f37c539", 190 "prevout_n": 1, 191 "value_sats": 1000000, 192 "privkey": "p2wpkh:cVDXzzQg6RoCTfiKpe8MBvmm5d5cJc6JLuFApsFDKwWa6F5TVHpD", 193 "nsequence": 0xfffffffd 194 } 195 ], 196 "outputs": [ 197 { 198 "address": "tb1q4s8z6g5jqzllkgt8a4har94wl8tg0k9m8kv5zd", 199 "value_sats": 990000 200 } 201 ] 202 } 203 self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000fdffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402201c551df0458528d19ba1dd79b134dcf0055f7b029dfc3d0d024e6253d069d13e02206d03cfc85a6fc648acb6fc6be630e4567d1dd00ddbcdee551ee0711414e2f33f0121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", 204 cmds._run('serialize', (jsontx,))) 205 206 @mock.patch.object(wallet.Abstract_Wallet, 'save_db') 207 def test_getprivatekeyforpath(self, mock_save_db): 208 wallet = restore_wallet_from_text('north rent dawn bunker hamster invest wagon market romance pig either squeeze', 209 gap_limit=2, 210 path='if_this_exists_mocking_failed_648151893', 211 config=self.config)['wallet'] 212 cmds = Commands(config=self.config) 213 self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2", 214 cmds._run('getprivatekeyforpath', ([0, 10000],), wallet=wallet)) 215 self.assertEqual("p2wpkh:cUzm7zPpWgLYeURgff4EsoMjhskCpsviBH4Y3aZcrBX8UJSRPjC2", 216 cmds._run('getprivatekeyforpath', ("m/0/10000",), wallet=wallet)) 217 self.assertEqual("p2wpkh:cQAj4WGf1socCPCJNMjXYCJ8Bs5JUAk5pbDr4ris44QdgAXcV24S", 218 cmds._run('getprivatekeyforpath', ("m/5h/100000/88h/7",), wallet=wallet))