test_bitcoin.py (53879B)
1 import base64 2 import sys 3 4 from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key, 5 is_address, is_private_key, 6 var_int, _op_push, address_to_script, 7 deserialize_privkey, serialize_privkey, is_segwit_address, 8 is_b58_address, address_to_scripthash, is_minikey, 9 is_compressed_privkey, EncodeBase58Check, DecodeBase58Check, 10 script_num_to_hex, push_script, add_number_to_script, int_to_hex, 11 opcodes, base_encode, base_decode, BitcoinException) 12 from electrum import bip32 13 from electrum.bip32 import (BIP32Node, convert_bip32_intpath_to_strpath, 14 xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation, 15 is_xpub, convert_bip32_path_to_list_of_uint32, 16 normalize_bip32_derivation, is_all_public_derivation) 17 from electrum.crypto import sha256d, SUPPORTED_PW_HASH_VERSIONS 18 from electrum import ecc, crypto, constants 19 from electrum.util import bfh, bh2u, InvalidPassword, randrange 20 from electrum.storage import WalletStorage 21 from electrum.keystore import xtype_from_derivation 22 23 from electrum import ecc_fast 24 25 from . import ElectrumTestCase 26 from . import TestCaseForTestnet 27 from . import FAST_TESTS 28 29 30 def needs_test_with_all_aes_implementations(func): 31 """Function decorator to run a unit test multiple times: 32 once with each AES implementation. 33 34 NOTE: this is inherently sequential; 35 tests running in parallel would break things 36 """ 37 def run_test(*args, **kwargs): 38 if FAST_TESTS: # if set, only run tests once, using fastest implementation 39 func(*args, **kwargs) 40 return 41 has_cryptodome = crypto.HAS_CRYPTODOME 42 has_cryptography = crypto.HAS_CRYPTOGRAPHY 43 has_pyaes = crypto.HAS_PYAES 44 try: 45 if has_pyaes: 46 (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, False, True 47 func(*args, **kwargs) # pyaes 48 if has_cryptodome: 49 (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = True, False, False 50 func(*args, **kwargs) # cryptodome 51 if has_cryptography: 52 (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY, crypto.HAS_PYAES) = False, True, False 53 func(*args, **kwargs) # cryptography 54 finally: 55 crypto.HAS_CRYPTODOME = has_cryptodome 56 crypto.HAS_CRYPTOGRAPHY = has_cryptography 57 crypto.HAS_PYAES = has_pyaes 58 return run_test 59 60 61 def needs_test_with_all_chacha20_implementations(func): 62 """Function decorator to run a unit test multiple times: 63 once with each ChaCha20/Poly1305 implementation. 64 65 NOTE: this is inherently sequential; 66 tests running in parallel would break things 67 """ 68 def run_test(*args, **kwargs): 69 if FAST_TESTS: # if set, only run tests once, using fastest implementation 70 func(*args, **kwargs) 71 return 72 has_cryptodome = crypto.HAS_CRYPTODOME 73 has_cryptography = crypto.HAS_CRYPTOGRAPHY 74 try: 75 if has_cryptodome: 76 (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = True, False 77 func(*args, **kwargs) # cryptodome 78 if has_cryptography: 79 (crypto.HAS_CRYPTODOME, crypto.HAS_CRYPTOGRAPHY) = False, True 80 func(*args, **kwargs) # cryptography 81 finally: 82 crypto.HAS_CRYPTODOME = has_cryptodome 83 crypto.HAS_CRYPTOGRAPHY = has_cryptography 84 return run_test 85 86 87 class Test_bitcoin(ElectrumTestCase): 88 89 def test_libsecp256k1_is_available(self): 90 # we want the unit testing framework to test with libsecp256k1 available. 91 self.assertTrue(bool(ecc_fast._libsecp256k1)) 92 93 def test_pycryptodomex_is_available(self): 94 # we want the unit testing framework to test with pycryptodomex available. 95 self.assertTrue(bool(crypto.HAS_CRYPTODOME)) 96 97 def test_cryptography_is_available(self): 98 # we want the unit testing framework to test with cryptography available. 99 self.assertTrue(bool(crypto.HAS_CRYPTOGRAPHY)) 100 101 def test_pyaes_is_available(self): 102 # we want the unit testing framework to test with pyaes available. 103 self.assertTrue(bool(crypto.HAS_PYAES)) 104 105 @needs_test_with_all_aes_implementations 106 def test_crypto(self): 107 for message in [b"Chancellor on brink of second bailout for banks", b'\xff'*512]: 108 self._do_test_crypto(message) 109 110 def _do_test_crypto(self, message): 111 G = ecc.GENERATOR 112 _r = G.order() 113 pvk = randrange(_r) 114 115 Pub = pvk*G 116 pubkey_c = Pub.get_public_key_bytes(True) 117 #pubkey_u = point_to_ser(Pub,False) 118 addr_c = public_key_to_p2pkh(pubkey_c) 119 120 #print "Private key ", '%064x'%pvk 121 eck = ecc.ECPrivkey.from_secret_scalar(pvk) 122 123 #print "Compressed public key ", pubkey_c.encode('hex') 124 enc = ecc.ECPubkey(pubkey_c).encrypt_message(message) 125 dec = eck.decrypt_message(enc) 126 self.assertEqual(message, dec) 127 128 #print "Uncompressed public key", pubkey_u.encode('hex') 129 #enc2 = EC_KEY.encrypt_message(message, pubkey_u) 130 dec2 = eck.decrypt_message(enc) 131 self.assertEqual(message, dec2) 132 133 signature = eck.sign_message(message, True) 134 #print signature 135 eck.verify_message_for_address(signature, message) 136 137 def test_ecc_sanity(self): 138 G = ecc.GENERATOR 139 n = G.order() 140 self.assertEqual(ecc.CURVE_ORDER, n) 141 inf = n * G 142 self.assertEqual(ecc.POINT_AT_INFINITY, inf) 143 self.assertTrue(inf.is_at_infinity()) 144 self.assertFalse(G.is_at_infinity()) 145 self.assertEqual(11 * G, 7 * G + 4 * G) 146 self.assertEqual((n + 2) * G, 2 * G) 147 self.assertEqual((n - 2) * G, -2 * G) 148 A = (n - 2) * G 149 B = (n - 1) * G 150 C = n * G 151 D = (n + 1) * G 152 self.assertFalse(A.is_at_infinity()) 153 self.assertFalse(B.is_at_infinity()) 154 self.assertTrue(C.is_at_infinity()) 155 self.assertTrue((C * 5).is_at_infinity()) 156 self.assertFalse(D.is_at_infinity()) 157 self.assertEqual(inf, C) 158 self.assertEqual(inf, A + 2 * G) 159 self.assertEqual(inf, D + (-1) * G) 160 self.assertNotEqual(A, B) 161 self.assertEqual(2 * G, inf + 2 * G) 162 self.assertEqual(inf, 3 * G + (-3 * G)) 163 164 def test_msg_signing(self): 165 msg1 = b'Chancellor on brink of second bailout for banks' 166 msg2 = b'Electrum' 167 168 def sign_message_with_wif_privkey(wif_privkey, msg): 169 txin_type, privkey, compressed = deserialize_privkey(wif_privkey) 170 key = ecc.ECPrivkey(privkey) 171 return key.sign_message(msg, compressed) 172 173 sig1 = sign_message_with_wif_privkey( 174 'L1TnU2zbNaAqMoVh65Cyvmcjzbrj41Gs9iTLcWbpJCMynXuap6UN', msg1) 175 addr1 = '15hETetDmcXm1mM4sEf7U2KXC9hDHFMSzz' 176 sig2 = sign_message_with_wif_privkey( 177 '5Hxn5C4SQuiV6e62A1MtZmbSeQyrLFhu5uYks62pU5VBUygK2KD', msg2) 178 addr2 = '1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6' 179 180 sig1_b64 = base64.b64encode(sig1) 181 sig2_b64 = base64.b64encode(sig2) 182 183 self.assertEqual(sig1_b64, b'Hzsu0U/THAsPz/MSuXGBKSULz2dTfmrg1NsAhFp+wH5aKfmX4Db7ExLGa7FGn0m6Mf43KsbEOWpvUUUBTM3Uusw=') 184 self.assertEqual(sig2_b64, b'HBQdYfv7kOrxmRewLJnG7sV6KlU71O04hUnE4tai97p7Pg+D+yKaWXsdGgHTrKw90caQMo/D6b//qX50ge9P9iI=') 185 186 self.assertTrue(ecc.verify_message_with_address(addr1, sig1, msg1)) 187 self.assertTrue(ecc.verify_message_with_address(addr2, sig2, msg2)) 188 189 self.assertFalse(ecc.verify_message_with_address(addr1, b'wrong', msg1)) 190 self.assertFalse(ecc.verify_message_with_address(addr1, sig2, msg1)) 191 192 @needs_test_with_all_aes_implementations 193 def test_decrypt_message(self): 194 key = WalletStorage.get_eckey_from_password('pw123') 195 self.assertEqual(b'me<(s_s)>age', key.decrypt_message(b'QklFMQMDFtgT3zWSQsa+Uie8H/WvfUjlu9UN9OJtTt3KlgKeSTi6SQfuhcg1uIz9hp3WIUOFGTLr4RNQBdjPNqzXwhkcPi2Xsbiw6UCNJncVPJ6QBg==')) 196 self.assertEqual(b'me<(s_s)>age', key.decrypt_message(b'QklFMQKXOXbylOQTSMGfo4MFRwivAxeEEkewWQrpdYTzjPhqjHcGBJwdIhB7DyRfRQihuXx1y0ZLLv7XxLzrILzkl/H4YUtZB4uWjuOAcmxQH4i/Og==')) 197 self.assertEqual(b'hey_there' * 100, key.decrypt_message(b'QklFMQLOOsabsXtGQH8edAa6VOUa5wX8/DXmxX9NyHoAx1a5bWgllayGRVPeI2bf0ZdWK0tfal0ap0ZIVKbd2eOJybqQkILqT6E1/Syzq0Zicyb/AA1eZNkcX5y4gzloxinw00ubCA8M7gcUjJpOqbnksATcJ5y2YYXcHMGGfGurWu6uJ/UyrNobRidWppRMW5yR9/6utyNvT6OHIolCMEf7qLcmtneoXEiz51hkRdZS7weNf9mGqSbz9a2NL3sdh1A0feHIjAZgcCKcAvksNUSauf0/FnIjzTyPRpjRDMeDC8Ci3sGiuO3cvpWJwhZfbjcS26KmBv2CHWXfRRNFYOInHZNIXWNAoBB47Il5bGSMd+uXiGr+SQ9tNvcu+BiJNmFbxYqg+oQ8dGAl1DtvY2wJVY8k7vO9BIWSpyIxfGw7EDifhc5vnOmGe016p6a01C3eVGxgl23UYMrP7+fpjOcPmTSF4rk5U5ljEN3MSYqlf1QEv0OqlI9q1TwTK02VBCjMTYxDHsnt04OjNBkNO8v5uJ4NR+UUDBEp433z53I59uawZ+dbk4v4ZExcl8EGmKm3Gzbal/iJ/F7KQuX2b/ySEhLOFVYFWxK73X1nBvCSK2mC2/8fCw8oI5pmvzJwQhcCKTdEIrz3MMvAHqtPScDUOjzhXxInQOCb3+UBj1PPIdqkYLvZss1TEaBwYZjLkVnK2MBj7BaqT6Rp6+5A/fippUKHsnB6eYMEPR2YgDmCHL+4twxHJG6UWdP3ybaKiiAPy2OHNP6PTZ0HrqHOSJzBSDD+Z8YpaRg29QX3UEWlqnSKaan0VYAsV1VeaN0XFX46/TWO0L5tjhYVXJJYGqo6tIQJymxATLFRF6AZaD1Mwd27IAL04WkmoQoXfO6OFfwdp/shudY/1gBkDBvGPICBPtnqkvhGF+ZF3IRkuPwiFWeXmwBxKHsRx/3+aJu32Ml9+za41zVk2viaxcGqwTc5KMexQFLAUwqhv+aIik7U+5qk/gEVSuRoVkihoweFzKolNF+BknH2oB4rZdPixag5Zje3DvgjsSFlOl69W/67t/Gs8htfSAaHlsB8vWRQr9+v/lxTbrAw+O0E+sYGoObQ4qQMyQshNZEHbpPg63eWiHtJJnrVBvOeIbIHzoLDnMDsWVWZSMzAQ1vhX1H5QLgSEbRlKSliVY03kDkh/Nk/KOn+B2q37Ialq4JcRoIYFGJ8AoYEAD0tRuTqFddIclE75HzwaNG7NyKW1plsa72ciOPwsPJsdd5F0qdSQ3OSKtooTn7uf6dXOc4lDkfrVYRlZ0PX')) 198 199 @needs_test_with_all_aes_implementations 200 def test_encrypt_message(self): 201 key = WalletStorage.get_eckey_from_password('secret_password77') 202 msgs = [ 203 bytes([0] * 555), 204 b'cannot think of anything funny' 205 ] 206 for plaintext in msgs: 207 ciphertext1 = key.encrypt_message(plaintext) 208 ciphertext2 = key.encrypt_message(plaintext) 209 self.assertEqual(plaintext, key.decrypt_message(ciphertext1)) 210 self.assertEqual(plaintext, key.decrypt_message(ciphertext2)) 211 self.assertNotEqual(ciphertext1, ciphertext2) 212 213 def test_sign_transaction(self): 214 eckey1 = ecc.ECPrivkey(bfh('7e1255fddb52db1729fc3ceb21a46f95b8d9fe94cc83425e936a6c5223bb679d')) 215 sig1 = eckey1.sign_transaction(bfh('5a548b12369a53faaa7e51b5081829474ebdd9c924b3a8230b69aa0be254cd94')) 216 self.assertEqual('3044022066e7d6a954006cce78a223f5edece8aaedcf3607142e9677acef1cfcb91cfdde022065cb0b5401bf16959ce7b785ea7fd408be5e4cb7d8f1b1a32c78eac6f73678d9', sig1.hex()) 217 218 eckey2 = ecc.ECPrivkey(bfh('c7ce8c1462c311eec24dff9e2532ac6241e50ae57e7d1833af21942136972f23')) 219 sig2 = eckey2.sign_transaction(bfh('642a2e66332f507c92bda910158dfe46fc10afbf72218764899d3af99a043fac')) 220 self.assertEqual('30440220618513f4cfc87dde798ce5febae7634c23e7b9254a1eabf486be820f6a7c2c4702204fef459393a2b931f949e63ced06888f35e286e446dc46feb24b5b5f81c6ed52', sig2.hex()) 221 222 @needs_test_with_all_aes_implementations 223 def test_aes_homomorphic(self): 224 """Make sure AES is homomorphic.""" 225 payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0' 226 password = u'secret' 227 for version in SUPPORTED_PW_HASH_VERSIONS: 228 enc = crypto.pw_encode(payload, password, version=version) 229 dec = crypto.pw_decode(enc, password, version=version) 230 self.assertEqual(dec, payload) 231 232 @needs_test_with_all_aes_implementations 233 def test_aes_encode_without_password(self): 234 """When not passed a password, pw_encode is noop on the payload.""" 235 payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0' 236 for version in SUPPORTED_PW_HASH_VERSIONS: 237 enc = crypto.pw_encode(payload, None, version=version) 238 self.assertEqual(payload, enc) 239 240 @needs_test_with_all_aes_implementations 241 def test_aes_deencode_without_password(self): 242 """When not passed a password, pw_decode is noop on the payload.""" 243 payload = u'\u66f4\u7a33\u5b9a\u7684\u4ea4\u6613\u5e73\u53f0' 244 for version in SUPPORTED_PW_HASH_VERSIONS: 245 enc = crypto.pw_decode(payload, None, version=version) 246 self.assertEqual(payload, enc) 247 248 @needs_test_with_all_aes_implementations 249 def test_aes_decode_with_invalid_password(self): 250 """pw_decode raises an Exception when supplied an invalid password.""" 251 payload = u"blah" 252 password = u"uber secret" 253 wrong_password = u"not the password" 254 for version in SUPPORTED_PW_HASH_VERSIONS: 255 enc = crypto.pw_encode(payload, password, version=version) 256 with self.assertRaises(InvalidPassword): 257 crypto.pw_decode(enc, wrong_password, version=version) 258 # sometimes the PKCS7 padding gets removed cleanly, 259 # but then UnicodeDecodeError gets raised (internally): 260 enc = 'smJ7j6ccr8LnMOlx98s/ajgikv9s3R1PQuG3GyyIMmo=' 261 with self.assertRaises(InvalidPassword): 262 crypto.pw_decode(enc, wrong_password, version=1) 263 264 @needs_test_with_all_chacha20_implementations 265 def test_chacha20_poly1305_encrypt__with_associated_data(self): 266 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 267 nonce = bytes.fromhex('010203040506070809101112') 268 associated_data = bytes.fromhex('30c9572d4305d4f3ccb766b1db884da6f1e0086f55136a39740700c272095717') 269 data = bytes.fromhex('4a6cd75da76cedf0a8a47e3a5734a328') 270 self.assertEqual(bytes.fromhex('90fb51fcde1fbe4013500bd7a32280445d80ee21f0aa3acd30df72cf609de064'), 271 crypto.chacha20_poly1305_encrypt(key=key, nonce=nonce, associated_data=associated_data, data=data)) 272 273 @needs_test_with_all_chacha20_implementations 274 def test_chacha20_poly1305_decrypt__with_associated_data(self): 275 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 276 nonce = bytes.fromhex('010203040506070809101112') 277 associated_data = bytes.fromhex('30c9572d4305d4f3ccb766b1db884da6f1e0086f55136a39740700c272095717') 278 data = bytes.fromhex('90fb51fcde1fbe4013500bd7a32280445d80ee21f0aa3acd30df72cf609de064') 279 self.assertEqual(bytes.fromhex('4a6cd75da76cedf0a8a47e3a5734a328'), 280 crypto.chacha20_poly1305_decrypt(key=key, nonce=nonce, associated_data=associated_data, data=data)) 281 with self.assertRaises(ValueError): 282 crypto.chacha20_poly1305_decrypt(key=key, nonce=nonce, associated_data=b'', data=data) 283 284 @needs_test_with_all_chacha20_implementations 285 def test_chacha20_poly1305_encrypt__without_associated_data(self): 286 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 287 nonce = bytes.fromhex('010203040506070809101112') 288 data = bytes.fromhex('4a6cd75da76cedf0a8a47e3a5734a328') 289 self.assertEqual(bytes.fromhex('90fb51fcde1fbe4013500bd7a322804469c2be9b1385bc5ded5cd96be510280f'), 290 crypto.chacha20_poly1305_encrypt(key=key, nonce=nonce, data=data)) 291 self.assertEqual(bytes.fromhex('90fb51fcde1fbe4013500bd7a322804469c2be9b1385bc5ded5cd96be510280f'), 292 crypto.chacha20_poly1305_encrypt(key=key, nonce=nonce, data=data, associated_data=b'')) 293 294 @needs_test_with_all_chacha20_implementations 295 def test_chacha20_poly1305_decrypt__without_associated_data(self): 296 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 297 nonce = bytes.fromhex('010203040506070809101112') 298 data = bytes.fromhex('90fb51fcde1fbe4013500bd7a322804469c2be9b1385bc5ded5cd96be510280f') 299 self.assertEqual(bytes.fromhex('4a6cd75da76cedf0a8a47e3a5734a328'), 300 crypto.chacha20_poly1305_decrypt(key=key, nonce=nonce, data=data)) 301 self.assertEqual(bytes.fromhex('4a6cd75da76cedf0a8a47e3a5734a328'), 302 crypto.chacha20_poly1305_decrypt(key=key, nonce=nonce, data=data, associated_data=b'')) 303 304 @needs_test_with_all_chacha20_implementations 305 def test_chacha20_encrypt__8_byte_nonce(self): 306 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 307 nonce = bytes.fromhex('0102030405060708') 308 data = bytes.fromhex('38a0e0a7c865fe9ca31f0730cfcab610f18e6da88dc3790f1d243f711a257c78') 309 ciphertext = crypto.chacha20_encrypt(key=key, nonce=nonce, data=data) 310 self.assertEqual(bytes.fromhex('f62fbd74d197323c7c3d5658476a884d38ee6f4b5500add1e8dc80dcd9c15dff'), ciphertext) 311 self.assertEqual(data, crypto.chacha20_decrypt(key=key, nonce=nonce, data=ciphertext)) 312 313 @needs_test_with_all_chacha20_implementations 314 def test_chacha20_encrypt__12_byte_nonce(self): 315 key = bytes.fromhex('37326d9d69a83b815ddfd947d21b0dd39111e5b6a5a44042c44d570ea03e3179') 316 nonce = bytes.fromhex('010203040506070809101112') 317 data = bytes.fromhex('38a0e0a7c865fe9ca31f0730cfcab610f18e6da88dc3790f1d243f711a257c78') 318 ciphertext = crypto.chacha20_encrypt(key=key, nonce=nonce, data=data) 319 self.assertEqual(bytes.fromhex('c0b1cb75c3c23c13f47dab393add738c92c62c4e2546cb3bf2b48269a4184028'), ciphertext) 320 self.assertEqual(data, crypto.chacha20_decrypt(key=key, nonce=nonce, data=ciphertext)) 321 322 def test_sha256d(self): 323 self.assertEqual(b'\x95MZI\xfdp\xd9\xb8\xbc\xdb5\xd2R&x)\x95\x7f~\xf7\xfalt\xf8\x84\x19\xbd\xc5\xe8"\t\xf4', 324 sha256d(u"test")) 325 326 def test_int_to_hex(self): 327 self.assertEqual('00', int_to_hex(0, 1)) 328 self.assertEqual('ff', int_to_hex(-1, 1)) 329 self.assertEqual('00000000', int_to_hex(0, 4)) 330 self.assertEqual('01000000', int_to_hex(1, 4)) 331 self.assertEqual('7f', int_to_hex(127, 1)) 332 self.assertEqual('7f00', int_to_hex(127, 2)) 333 self.assertEqual('80', int_to_hex(128, 1)) 334 self.assertEqual('80', int_to_hex(-128, 1)) 335 self.assertEqual('8000', int_to_hex(128, 2)) 336 self.assertEqual('ff', int_to_hex(255, 1)) 337 self.assertEqual('ff7f', int_to_hex(32767, 2)) 338 self.assertEqual('0080', int_to_hex(-32768, 2)) 339 self.assertEqual('ffff', int_to_hex(65535, 2)) 340 with self.assertRaises(OverflowError): int_to_hex(256, 1) 341 with self.assertRaises(OverflowError): int_to_hex(-129, 1) 342 with self.assertRaises(OverflowError): int_to_hex(-257, 1) 343 with self.assertRaises(OverflowError): int_to_hex(65536, 2) 344 with self.assertRaises(OverflowError): int_to_hex(-32769, 2) 345 346 def test_var_int(self): 347 for i in range(0xfd): 348 self.assertEqual(var_int(i), "{:02x}".format(i) ) 349 350 self.assertEqual(var_int(0xfd), "fdfd00") 351 self.assertEqual(var_int(0xfe), "fdfe00") 352 self.assertEqual(var_int(0xff), "fdff00") 353 self.assertEqual(var_int(0x1234), "fd3412") 354 self.assertEqual(var_int(0xffff), "fdffff") 355 self.assertEqual(var_int(0x10000), "fe00000100") 356 self.assertEqual(var_int(0x12345678), "fe78563412") 357 self.assertEqual(var_int(0xffffffff), "feffffffff") 358 self.assertEqual(var_int(0x100000000), "ff0000000001000000") 359 self.assertEqual(var_int(0x0123456789abcdef), "ffefcdab8967452301") 360 361 def test_op_push(self): 362 self.assertEqual(_op_push(0x00), '00') 363 self.assertEqual(_op_push(0x12), '12') 364 self.assertEqual(_op_push(0x4b), '4b') 365 self.assertEqual(_op_push(0x4c), '4c4c') 366 self.assertEqual(_op_push(0xfe), '4cfe') 367 self.assertEqual(_op_push(0xff), '4cff') 368 self.assertEqual(_op_push(0x100), '4d0001') 369 self.assertEqual(_op_push(0x1234), '4d3412') 370 self.assertEqual(_op_push(0xfffe), '4dfeff') 371 self.assertEqual(_op_push(0xffff), '4dffff') 372 self.assertEqual(_op_push(0x10000), '4e00000100') 373 self.assertEqual(_op_push(0x12345678), '4e78563412') 374 375 def test_script_num_to_hex(self): 376 # test vectors from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptnum.go#L77 377 self.assertEqual(script_num_to_hex(127), '7f') 378 self.assertEqual(script_num_to_hex(-127), 'ff') 379 self.assertEqual(script_num_to_hex(128), '8000') 380 self.assertEqual(script_num_to_hex(-128), '8080') 381 self.assertEqual(script_num_to_hex(129), '8100') 382 self.assertEqual(script_num_to_hex(-129), '8180') 383 self.assertEqual(script_num_to_hex(256), '0001') 384 self.assertEqual(script_num_to_hex(-256), '0081') 385 self.assertEqual(script_num_to_hex(32767), 'ff7f') 386 self.assertEqual(script_num_to_hex(-32767), 'ffff') 387 self.assertEqual(script_num_to_hex(32768), '008000') 388 self.assertEqual(script_num_to_hex(-32768), '008080') 389 390 def test_push_script(self): 391 # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#push-operators 392 self.assertEqual(push_script(''), bh2u(bytes([opcodes.OP_0]))) 393 self.assertEqual(push_script('07'), bh2u(bytes([opcodes.OP_7]))) 394 self.assertEqual(push_script('10'), bh2u(bytes([opcodes.OP_16]))) 395 self.assertEqual(push_script('81'), bh2u(bytes([opcodes.OP_1NEGATE]))) 396 self.assertEqual(push_script('11'), '0111') 397 self.assertEqual(push_script(75 * '42'), '4b' + 75 * '42') 398 self.assertEqual(push_script(76 * '42'), bh2u(bytes([opcodes.OP_PUSHDATA1]) + bfh('4c' + 76 * '42'))) 399 self.assertEqual(push_script(100 * '42'), bh2u(bytes([opcodes.OP_PUSHDATA1]) + bfh('64' + 100 * '42'))) 400 self.assertEqual(push_script(255 * '42'), bh2u(bytes([opcodes.OP_PUSHDATA1]) + bfh('ff' + 255 * '42'))) 401 self.assertEqual(push_script(256 * '42'), bh2u(bytes([opcodes.OP_PUSHDATA2]) + bfh('0001' + 256 * '42'))) 402 self.assertEqual(push_script(520 * '42'), bh2u(bytes([opcodes.OP_PUSHDATA2]) + bfh('0802' + 520 * '42'))) 403 404 def test_add_number_to_script(self): 405 # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#numbers 406 self.assertEqual(add_number_to_script(0), bytes([opcodes.OP_0])) 407 self.assertEqual(add_number_to_script(7), bytes([opcodes.OP_7])) 408 self.assertEqual(add_number_to_script(16), bytes([opcodes.OP_16])) 409 self.assertEqual(add_number_to_script(-1), bytes([opcodes.OP_1NEGATE])) 410 self.assertEqual(add_number_to_script(-127), bfh('01ff')) 411 self.assertEqual(add_number_to_script(-2), bfh('0182')) 412 self.assertEqual(add_number_to_script(17), bfh('0111')) 413 self.assertEqual(add_number_to_script(127), bfh('017f')) 414 self.assertEqual(add_number_to_script(-32767), bfh('02ffff')) 415 self.assertEqual(add_number_to_script(-128), bfh('028080')) 416 self.assertEqual(add_number_to_script(128), bfh('028000')) 417 self.assertEqual(add_number_to_script(32767), bfh('02ff7f')) 418 self.assertEqual(add_number_to_script(-8388607), bfh('03ffffff')) 419 self.assertEqual(add_number_to_script(-32768), bfh('03008080')) 420 self.assertEqual(add_number_to_script(32768), bfh('03008000')) 421 self.assertEqual(add_number_to_script(8388607), bfh('03ffff7f')) 422 self.assertEqual(add_number_to_script(-2147483647), bfh('04ffffffff')) 423 self.assertEqual(add_number_to_script(-8388608 ), bfh('0400008080')) 424 self.assertEqual(add_number_to_script(8388608), bfh('0400008000')) 425 self.assertEqual(add_number_to_script(2147483647), bfh('04ffffff7f')) 426 427 def test_address_to_script(self): 428 # bech32 native segwit 429 # test vectors from BIP-0173 430 self.assertEqual(address_to_script('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'), '0014751e76e8199196d454941c45d1b3a323f1433bd6') 431 self.assertEqual(address_to_script('bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'), '5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6') 432 self.assertEqual(address_to_script('BC1SW50QA3JX3S'), '6002751e') 433 self.assertEqual(address_to_script('bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj'), '5210751e76e8199196d454941c45d1b3a323') 434 435 # base58 P2PKH 436 self.assertEqual(address_to_script('14gcRovpkCoGkCNBivQBvw7eso7eiNAbxG'), '76a91428662c67561b95c79d2257d2a93d9d151c977e9188ac') 437 self.assertEqual(address_to_script('1BEqfzh4Y3zzLosfGhw1AsqbEKVW6e1qHv'), '76a914704f4b81cadb7bf7e68c08cd3657220f680f863c88ac') 438 439 # base58 P2SH 440 self.assertEqual(address_to_script('35ZqQJcBQMZ1rsv8aSuJ2wkC7ohUCQMJbT'), 'a9142a84cf00d47f699ee7bbc1dea5ec1bdecb4ac15487') 441 self.assertEqual(address_to_script('3PyjzJ3im7f7bcV724GR57edKDqoZvH7Ji'), 'a914f47c8954e421031ad04ecd8e7752c9479206b9d387') 442 443 444 class Test_bitcoin_testnet(TestCaseForTestnet): 445 446 def test_address_to_script(self): 447 # bech32 native segwit 448 # test vectors from BIP-0173 449 self.assertEqual(address_to_script('tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7'), '00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262') 450 self.assertEqual(address_to_script('tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy'), '0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433') 451 452 # base58 P2PKH 453 self.assertEqual(address_to_script('mutXcGt1CJdkRvXuN2xoz2quAAQYQ59bRX'), '76a9149da64e300c5e4eb4aaffc9c2fd465348d5618ad488ac') 454 self.assertEqual(address_to_script('miqtaRTkU3U8rzwKbEHx3g8FSz8GJtPS3K'), '76a914247d2d5b6334bdfa2038e85b20fc15264f8e5d2788ac') 455 456 # base58 P2SH 457 self.assertEqual(address_to_script('2N3LSvr3hv5EVdfcrxg2Yzecf3SRvqyBE4p'), 'a9146eae23d8c4a941316017946fc761a7a6c85561fb87') 458 self.assertEqual(address_to_script('2NE4ZdmxFmUgwu5wtfoN2gVniyMgRDYq1kk'), 'a914e4567743d378957cd2ee7072da74b1203c1a7a0b87') 459 460 461 class Test_xprv_xpub(ElectrumTestCase): 462 463 xprv_xpub = ( 464 # Taken from test vectors in https://en.bitcoin.it/wiki/BIP_0032_TestVectors 465 {'xprv': 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', 466 'xpub': 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy', 467 'xtype': 'standard'}, 468 {'xprv': 'yprvAJEYHeNEPcyBoQYM7sGCxDiNCTX65u4ANgZuSGTrKN5YCC9MP84SBayrgaMyZV7zvkHrr3HVPTK853s2SPk4EttPazBZBmz6QfDkXeE8Zr7', 469 'xpub': 'ypub6XDth9u8DzXV1tcpDtoDKMf6kVMaVMn1juVWEesTshcX4zUVvfNgjPJLXrD9N7AdTLnbHFL64KmBn3SNaTe69iZYbYCqLCCNPZKbLz9niQ4', 470 'xtype': 'p2wpkh-p2sh'}, 471 {'xprv': 'zprvAWgYBBk7JR8GkraNZJeEodAp2UR1VRWJTXyV1ywuUVs1awUgTiBS1ZTDtLA5F3MFDn1LZzu8dUpSKdT7ToDpvEG6PQu4bJs7zQY47Sd3sEZ', 472 'xpub': 'zpub6jftahH18ngZyLeqfLBFAm7YaWFVttE9pku5pNMX2qPzTjoq1FVgZMmhjecyB2nqFb31gHE9vNvbaggU6vvWpNZbXEWLLUjYjFqG95LNyT8', 473 'xtype': 'p2wpkh'}, 474 ) 475 476 def _do_test_bip32(self, seed: str, sequence: str): 477 node = BIP32Node.from_rootseed(bfh(seed), xtype='standard') 478 xprv, xpub = node.to_xprv(), node.to_xpub() 479 int_path = convert_bip32_path_to_list_of_uint32(sequence) 480 for n in int_path: 481 if n & bip32.BIP32_PRIME == 0: 482 xpub2 = BIP32Node.from_xkey(xpub).subkey_at_public_derivation([n]).to_xpub() 483 node = BIP32Node.from_xkey(xprv).subkey_at_private_derivation([n]) 484 xprv, xpub = node.to_xprv(), node.to_xpub() 485 if n & bip32.BIP32_PRIME == 0: 486 self.assertEqual(xpub, xpub2) 487 488 return xpub, xprv 489 490 def test_bip32(self): 491 # see https://en.bitcoin.it/wiki/BIP_0032_TestVectors 492 # and https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors 493 xpub, xprv = self._do_test_bip32("000102030405060708090a0b0c0d0e0f", "m/0'/1/2'/2/1000000000") 494 self.assertEqual("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", xpub) 495 self.assertEqual("xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", xprv) 496 497 xpub, xprv = self._do_test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542","m/0/2147483647'/1/2147483646'/2") 498 self.assertEqual("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", xpub) 499 self.assertEqual("xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", xprv) 500 501 xpub, xprv = self._do_test_bip32("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be", "m/0h") 502 self.assertEqual("xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", xpub) 503 self.assertEqual("xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", xprv) 504 505 xpub, xprv = self._do_test_bip32("3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678", "m/0h/1h") 506 self.assertEqual("xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt", xpub) 507 self.assertEqual("xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1", xprv) 508 509 def test_xpub_from_xprv(self): 510 """We can derive the xpub key from a xprv.""" 511 for xprv_details in self.xprv_xpub: 512 result = xpub_from_xprv(xprv_details['xprv']) 513 self.assertEqual(result, xprv_details['xpub']) 514 515 def test_is_xpub(self): 516 for xprv_details in self.xprv_xpub: 517 xpub = xprv_details['xpub'] 518 self.assertTrue(is_xpub(xpub)) 519 self.assertFalse(is_xpub('xpub1nval1d')) 520 self.assertFalse(is_xpub('xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52WRONGBADWRONG')) 521 522 def test_xpub_type(self): 523 for xprv_details in self.xprv_xpub: 524 xpub = xprv_details['xpub'] 525 self.assertEqual(xprv_details['xtype'], xpub_type(xpub)) 526 527 def test_is_xprv(self): 528 for xprv_details in self.xprv_xpub: 529 xprv = xprv_details['xprv'] 530 self.assertTrue(is_xprv(xprv)) 531 self.assertFalse(is_xprv('xprv1nval1d')) 532 self.assertFalse(is_xprv('xprv661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52WRONGBADWRONG')) 533 534 def test_is_bip32_derivation(self): 535 self.assertTrue(is_bip32_derivation("m/0'/1")) 536 self.assertTrue(is_bip32_derivation("m/0'/0'")) 537 self.assertTrue(is_bip32_derivation("m/3'/-5/8h/")) 538 self.assertTrue(is_bip32_derivation("m/44'/0'/0'/0/0")) 539 self.assertTrue(is_bip32_derivation("m/49'/0'/0'/0/0")) 540 self.assertTrue(is_bip32_derivation("m")) 541 self.assertTrue(is_bip32_derivation("m/")) 542 self.assertFalse(is_bip32_derivation("m5")) 543 self.assertFalse(is_bip32_derivation("mmmmmm")) 544 self.assertFalse(is_bip32_derivation("n/")) 545 self.assertFalse(is_bip32_derivation("")) 546 self.assertFalse(is_bip32_derivation("m/q8462")) 547 self.assertFalse(is_bip32_derivation("m/-8h")) 548 549 def test_convert_bip32_path_to_list_of_uint32(self): 550 self.assertEqual([0, 0x80000001, 0x80000001], convert_bip32_path_to_list_of_uint32("m/0/-1/1'")) 551 self.assertEqual([], convert_bip32_path_to_list_of_uint32("m/")) 552 self.assertEqual([2147483692, 2147488889, 221], convert_bip32_path_to_list_of_uint32("m/44'/5241h/221")) 553 554 def test_convert_bip32_intpath_to_strpath(self): 555 self.assertEqual("m/0/1'/1'", convert_bip32_intpath_to_strpath([0, 0x80000001, 0x80000001])) 556 self.assertEqual("m", convert_bip32_intpath_to_strpath([])) 557 self.assertEqual("m/44'/5241'/221", convert_bip32_intpath_to_strpath([2147483692, 2147488889, 221])) 558 559 def test_normalize_bip32_derivation(self): 560 self.assertEqual("m/0/1'/1'", normalize_bip32_derivation("m/0/1h/1'")) 561 self.assertEqual("m", normalize_bip32_derivation("m////")) 562 self.assertEqual("m/0/2/1'", normalize_bip32_derivation("m/0/2/-1/")) 563 self.assertEqual("m/0/1'/1'/5'", normalize_bip32_derivation("m/0//-1/1'///5h")) 564 565 def test_is_xkey_consistent_with_key_origin_info(self): 566 ### actual data (high depth path) 567 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 568 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 569 derivation_prefix="m/48'/1'/0'/2'", 570 root_fingerprint="b2768d2f")) 571 # ok to skip args 572 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 573 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 574 derivation_prefix="m/48'/1'/0'/2'")) 575 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 576 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 577 root_fingerprint="b2768d2f")) 578 # path changed: wrong depth 579 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 580 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 581 derivation_prefix="m/48'/0'/2'", 582 root_fingerprint="b2768d2f")) 583 # path changed: wrong child index 584 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 585 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 586 derivation_prefix="m/48'/1'/0'/3'", 587 root_fingerprint="b2768d2f")) 588 # path changed: but cannot tell 589 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 590 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 591 derivation_prefix="m/48'/1'/1'/2'", 592 root_fingerprint="b2768d2f")) 593 # fp changed: but cannot tell 594 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 595 "Zpub75NQordWKAkaF7utBw95GEodyxqwFdR3idtTqQtrvWkYFeiuYdg5c3Q9L9bLjPLhEahLCTjmmS2YQcXPwr6twYCEJ55k6uhE5JxRqvUowmd", 596 derivation_prefix="m/48'/1'/0'/2'", 597 root_fingerprint="aaaaaaaa")) 598 599 ### actual data (depth=1 path) 600 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 601 "zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ", 602 derivation_prefix="m/0'", 603 root_fingerprint="b2e35a7d")) 604 # path changed: wrong depth 605 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 606 "zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ", 607 derivation_prefix="m/0'/0'", 608 root_fingerprint="b2e35a7d")) 609 # path changed: wrong child index 610 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 611 "zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ", 612 derivation_prefix="m/1'", 613 root_fingerprint="b2e35a7d")) 614 # fp changed: can tell 615 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 616 "zpub6nsHdRuY92FsMKdbn9BfjBCG6X8pyhCibNP6uDvpnw2cyrVhecvHRMa3Ne8kdJZxjxgwnpbHLkcR4bfnhHy6auHPJyDTQ3kianeuVLdkCYQ", 617 derivation_prefix="m/0'", 618 root_fingerprint="aaaaaaaa")) 619 620 ### actual data (depth=0 path) 621 self.assertTrue(bip32.is_xkey_consistent_with_key_origin_info( 622 "xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U", 623 derivation_prefix="m", 624 root_fingerprint="48adc7a0")) 625 # path changed: wrong depth 626 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 627 "xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U", 628 derivation_prefix="m/0", 629 root_fingerprint="48adc7a0")) 630 # fp changed: can tell 631 self.assertFalse(bip32.is_xkey_consistent_with_key_origin_info( 632 "xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U", 633 derivation_prefix="m", 634 root_fingerprint="aaaaaaaa")) 635 636 def test_is_all_public_derivation(self): 637 self.assertFalse(is_all_public_derivation("m/0/1'/1'")) 638 self.assertFalse(is_all_public_derivation("m/0/2/1'")) 639 self.assertFalse(is_all_public_derivation("m/0/1'/1'/5")) 640 self.assertTrue(is_all_public_derivation("m")) 641 self.assertTrue(is_all_public_derivation("m/0")) 642 self.assertTrue(is_all_public_derivation("m/75/22/3")) 643 644 def test_xtype_from_derivation(self): 645 self.assertEqual('standard', xtype_from_derivation("m/44'")) 646 self.assertEqual('standard', xtype_from_derivation("m/44'/")) 647 self.assertEqual('standard', xtype_from_derivation("m/44'/0'/0'")) 648 self.assertEqual('standard', xtype_from_derivation("m/44'/5241'/221")) 649 self.assertEqual('standard', xtype_from_derivation("m/45'")) 650 self.assertEqual('standard', xtype_from_derivation("m/45'/56165/271'")) 651 self.assertEqual('p2wpkh-p2sh', xtype_from_derivation("m/49'")) 652 self.assertEqual('p2wpkh-p2sh', xtype_from_derivation("m/49'/134")) 653 self.assertEqual('p2wpkh', xtype_from_derivation("m/84'")) 654 self.assertEqual('p2wpkh', xtype_from_derivation("m/84'/112'/992/112/33'/0/2")) 655 self.assertEqual('p2wsh-p2sh', xtype_from_derivation("m/48'/0'/0'/1'")) 656 self.assertEqual('p2wsh-p2sh', xtype_from_derivation("m/48'/0'/0'/1'/52112/52'")) 657 self.assertEqual('p2wsh-p2sh', xtype_from_derivation("m/48'/9'/2'/1'")) 658 self.assertEqual('p2wsh', xtype_from_derivation("m/48'/0'/0'/2'")) 659 self.assertEqual('p2wsh', xtype_from_derivation("m/48'/1'/0'/2'/77'/0")) 660 661 def test_version_bytes(self): 662 xprv_headers_b58 = { 663 'standard': 'xprv', 664 'p2wpkh-p2sh': 'yprv', 665 'p2wsh-p2sh': 'Yprv', 666 'p2wpkh': 'zprv', 667 'p2wsh': 'Zprv', 668 } 669 xpub_headers_b58 = { 670 'standard': 'xpub', 671 'p2wpkh-p2sh': 'ypub', 672 'p2wsh-p2sh': 'Ypub', 673 'p2wpkh': 'zpub', 674 'p2wsh': 'Zpub', 675 } 676 for xtype, xkey_header_bytes in constants.net.XPRV_HEADERS.items(): 677 xkey_header_bytes = bfh("%08x" % xkey_header_bytes) 678 xkey_bytes = xkey_header_bytes + bytes([0] * 74) 679 xkey_b58 = EncodeBase58Check(xkey_bytes) 680 self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype])) 681 682 xkey_bytes = xkey_header_bytes + bytes([255] * 74) 683 xkey_b58 = EncodeBase58Check(xkey_bytes) 684 self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype])) 685 686 for xtype, xkey_header_bytes in constants.net.XPUB_HEADERS.items(): 687 xkey_header_bytes = bfh("%08x" % xkey_header_bytes) 688 xkey_bytes = xkey_header_bytes + bytes([0] * 74) 689 xkey_b58 = EncodeBase58Check(xkey_bytes) 690 self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype])) 691 692 xkey_bytes = xkey_header_bytes + bytes([255] * 74) 693 xkey_b58 = EncodeBase58Check(xkey_bytes) 694 self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype])) 695 696 697 class Test_xprv_xpub_testnet(TestCaseForTestnet): 698 699 def test_version_bytes(self): 700 xprv_headers_b58 = { 701 'standard': 'tprv', 702 'p2wpkh-p2sh': 'uprv', 703 'p2wsh-p2sh': 'Uprv', 704 'p2wpkh': 'vprv', 705 'p2wsh': 'Vprv', 706 } 707 xpub_headers_b58 = { 708 'standard': 'tpub', 709 'p2wpkh-p2sh': 'upub', 710 'p2wsh-p2sh': 'Upub', 711 'p2wpkh': 'vpub', 712 'p2wsh': 'Vpub', 713 } 714 for xtype, xkey_header_bytes in constants.net.XPRV_HEADERS.items(): 715 xkey_header_bytes = bfh("%08x" % xkey_header_bytes) 716 xkey_bytes = xkey_header_bytes + bytes([0] * 74) 717 xkey_b58 = EncodeBase58Check(xkey_bytes) 718 self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype])) 719 720 xkey_bytes = xkey_header_bytes + bytes([255] * 74) 721 xkey_b58 = EncodeBase58Check(xkey_bytes) 722 self.assertTrue(xkey_b58.startswith(xprv_headers_b58[xtype])) 723 724 for xtype, xkey_header_bytes in constants.net.XPUB_HEADERS.items(): 725 xkey_header_bytes = bfh("%08x" % xkey_header_bytes) 726 xkey_bytes = xkey_header_bytes + bytes([0] * 74) 727 xkey_b58 = EncodeBase58Check(xkey_bytes) 728 self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype])) 729 730 xkey_bytes = xkey_header_bytes + bytes([255] * 74) 731 xkey_b58 = EncodeBase58Check(xkey_bytes) 732 self.assertTrue(xkey_b58.startswith(xpub_headers_b58[xtype])) 733 734 735 class Test_keyImport(ElectrumTestCase): 736 737 priv_pub_addr = ( 738 {'priv': 'KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6', 739 'exported_privkey': 'p2pkh:KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6', 740 'pub': '02c6467b7e621144105ed3e4835b0b4ab7e35266a2ae1c4f8baa19e9ca93452997', 741 'address': '17azqT8T16coRmWKYFj3UjzJuxiYrYFRBR', 742 'minikey' : False, 743 'txin_type': 'p2pkh', 744 'compressed': True, 745 'addr_encoding': 'base58', 746 'scripthash': 'c9aecd1fef8d661a42c560bf75c8163e337099800b8face5ca3d1393a30508a7'}, 747 {'priv': 'p2pkh:Kzj8VjwpZ99bQqVeUiRXrKuX9mLr1o6sWxFMCBJn1umC38BMiQTD', 748 'exported_privkey': 'p2pkh:Kzj8VjwpZ99bQqVeUiRXrKuX9mLr1o6sWxFMCBJn1umC38BMiQTD', 749 'pub': '0352d78b4b37e0f6d4e164423436f2925fa57817467178eca550a88f2821973c41', 750 'address': '1GXgZ5Qi6gmXTHVSpUPZLy4Ci2nbfb3ZNb', 751 'minikey': False, 752 'txin_type': 'p2pkh', 753 'compressed': True, 754 'addr_encoding': 'base58', 755 'scripthash': 'a9b2a76fc196c553b352186dfcca81fcf323a721cd8431328f8e9d54216818c1'}, 756 {'priv': '5Hxn5C4SQuiV6e62A1MtZmbSeQyrLFhu5uYks62pU5VBUygK2KD', 757 'exported_privkey': 'p2pkh:5Hxn5C4SQuiV6e62A1MtZmbSeQyrLFhu5uYks62pU5VBUygK2KD', 758 'pub': '04e5fe91a20fac945845a5518450d23405ff3e3e1ce39827b47ee6d5db020a9075422d56a59195ada0035e4a52a238849f68e7a325ba5b2247013e0481c5c7cb3f', 759 'address': '1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6', 760 'minikey': False, 761 'txin_type': 'p2pkh', 762 'compressed': False, 763 'addr_encoding': 'base58', 764 'scripthash': 'f5914651408417e1166f725a5829ff9576d0dbf05237055bf13abd2af7f79473'}, 765 {'priv': 'p2pkh:5KhYQCe1xd5g2tqpmmGpUWDpDuTbA8vnpbiCNDwMPAx29WNQYfN', 766 'exported_privkey': 'p2pkh:5KhYQCe1xd5g2tqpmmGpUWDpDuTbA8vnpbiCNDwMPAx29WNQYfN', 767 'pub': '048f0431b0776e8210376c81280011c2b68be43194cb00bd47b7e9aa66284b713ce09556cde3fee606051a07613f3c159ef3953b8927c96ae3dae94a6ba4182e0e', 768 'address': '147kiRHHm9fqeMQSgqf4k35XzuWLP9fmmS', 769 'minikey': False, 770 'txin_type': 'p2pkh', 771 'compressed': False, 772 'addr_encoding': 'base58', 773 'scripthash': '6dd2e07ad2de9ba8eec4bbe8467eb53f8845acff0d9e6f5627391acc22ff62df'}, 774 {'priv': 'LHJnnvRzsdrTX2j5QeWVsaBkabK7gfMNqNNqxnbBVRaJYfk24iJz', 775 'exported_privkey': 'p2wpkh-p2sh:Kz9XebiCXL2BZzhYJViiHDzn5iup1povWV8aqstzWU4sz1K5nVva', 776 'pub': '0279ad237ca0d812fb503ab86f25e15ebd5fa5dd95c193639a8a738dcd1acbad81', 777 'address': '3GeVJB3oKr7psgKR6BTXSxKtWUkfsHHhk7', 778 'minikey': False, 779 'txin_type': 'p2wpkh-p2sh', 780 'compressed': True, 781 'addr_encoding': 'base58', 782 'scripthash': 'd7b04e882fa6b13246829ac552a2b21461d9152eb00f0a6adb58457a3e63d7c5'}, 783 {'priv': 'p2wpkh-p2sh:L3CZH1pm87X4bbE6mSGvZnAZ1KcFDRomBudUkrkBG7EZhDtBVXMW', 784 'exported_privkey': 'p2wpkh-p2sh:L3CZH1pm87X4bbE6mSGvZnAZ1KcFDRomBudUkrkBG7EZhDtBVXMW', 785 'pub': '0229da20a15b3363b2c28e3c5093c180b56c439df0b968a970366bb1f38435361e', 786 'address': '3C79goMwT7zSTjXnPoCg6VFGAnUpZAkyus', 787 'minikey': False, 788 'txin_type': 'p2wpkh-p2sh', 789 'compressed': True, 790 'addr_encoding': 'base58', 791 'scripthash': '714bf6bfe1083e69539f40d4c7a7dca85d187471b35642e55f20d7e866494cf7'}, 792 {'priv': 'L8g5V8kFFeg2WbecahRSdobARbHz2w2STH9S8ePHVSY4fmia7Rsj', 793 'exported_privkey': 'p2wpkh:Kz6SuyPM5VktY5dr2d2YqdVgBA6LCWkiHqXJaC3BzxnMPSUuYzmF', 794 'pub': '03e9f948421aaa89415dc5f281a61b60dde12aae3181b3a76cd2d849b164fc6d0b', 795 'address': 'bc1qqmpt7u5e9hfznljta5gnvhyvfd2kdd0r90hwue', 796 'minikey': False, 797 'txin_type': 'p2wpkh', 798 'compressed': True, 799 'addr_encoding': 'bech32', 800 'scripthash': '1929acaaef3a208c715228e9f1ca0318e3a6b9394ab53c8d026137f847ecf97b'}, 801 {'priv': 'p2wpkh:KyDWy5WbjLA58Zesh1o8m3pADGdJ3v33DKk4m7h8BD5zDKDmDFwo', 802 'exported_privkey': 'p2wpkh:KyDWy5WbjLA58Zesh1o8m3pADGdJ3v33DKk4m7h8BD5zDKDmDFwo', 803 'pub': '038c57657171c1f73e34d5b3971d05867d50221ad94980f7e87cbc2344425e6a1e', 804 'address': 'bc1qpakeeg4d9ydyjxd8paqrw4xy9htsg532xzxn50', 805 'minikey': False, 806 'txin_type': 'p2wpkh', 807 'compressed': True, 808 'addr_encoding': 'bech32', 809 'scripthash': '242f02adde84ebb2a7dd778b2f3a81b3826f111da4d8960d826d7a4b816cb261'}, 810 # from http://bitscan.com/articles/security/spotlight-on-mini-private-keys 811 {'priv': 'SzavMBLoXU6kDrqtUVmffv', 812 'exported_privkey': 'p2pkh:5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF', 813 'pub': '04588d202afcc1ee4ab5254c7847ec25b9a135bbda0f2bc69ee1a714749fd77dc9f88ff2a00d7e752d44cbe16e1ebcf0890b76ec7c78886109dee76ccfc8445424', 814 'address': '1CC3X2gu58d6wXUWMffpuzN9JAfTUWu4Kj', 815 'minikey': True, 816 'txin_type': 'p2pkh', 817 'compressed': False, # this is actually ambiguous... issue #2748 818 'addr_encoding': 'base58', 819 'scripthash': '5b07ddfde826f5125ee823900749103cea37808038ecead5505a766a07c34445'}, 820 ) 821 822 def test_public_key_from_private_key(self): 823 for priv_details in self.priv_pub_addr: 824 txin_type, privkey, compressed = deserialize_privkey(priv_details['priv']) 825 result = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) 826 self.assertEqual(priv_details['pub'], result) 827 self.assertEqual(priv_details['txin_type'], txin_type) 828 self.assertEqual(priv_details['compressed'], compressed) 829 830 def test_address_from_private_key(self): 831 for priv_details in self.priv_pub_addr: 832 addr2 = address_from_private_key(priv_details['priv']) 833 self.assertEqual(priv_details['address'], addr2) 834 835 def test_is_valid_address(self): 836 for priv_details in self.priv_pub_addr: 837 addr = priv_details['address'] 838 self.assertFalse(is_address(priv_details['priv'])) 839 self.assertFalse(is_address(priv_details['pub'])) 840 self.assertTrue(is_address(addr)) 841 842 is_enc_b58 = priv_details['addr_encoding'] == 'base58' 843 self.assertEqual(is_enc_b58, is_b58_address(addr)) 844 845 is_enc_bech32 = priv_details['addr_encoding'] == 'bech32' 846 self.assertEqual(is_enc_bech32, is_segwit_address(addr)) 847 848 self.assertFalse(is_address("not an address")) 849 850 def test_is_address_bad_checksums(self): 851 self.assertTrue(is_address('1819s5TxxbBtuRPr3qYskMVC8sb1pqapWx')) 852 self.assertFalse(is_address('1819s5TxxbBtuRPr3qYskMVC8sb1pqapWw')) 853 854 self.assertTrue(is_address('3LrjLVnngqnaJeo3BQwMBg34iqYsjZjQUe')) 855 self.assertFalse(is_address('3LrjLVnngqnaJeo3BQwMBg34iqYsjZjQUd')) 856 857 self.assertTrue(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt6')) 858 self.assertFalse(is_address('bc1qxq64lrwt02hm7tu25lr3hm9tgzh58snfe67yt5')) 859 860 def test_is_private_key(self): 861 for priv_details in self.priv_pub_addr: 862 self.assertTrue(is_private_key(priv_details['priv'])) 863 self.assertTrue(is_private_key(priv_details['exported_privkey'])) 864 self.assertFalse(is_private_key(priv_details['pub'])) 865 self.assertFalse(is_private_key(priv_details['address'])) 866 self.assertFalse(is_private_key("not a privkey")) 867 868 def test_serialize_privkey(self): 869 for priv_details in self.priv_pub_addr: 870 txin_type, privkey, compressed = deserialize_privkey(priv_details['priv']) 871 priv2 = serialize_privkey(privkey, compressed, txin_type) 872 self.assertEqual(priv_details['exported_privkey'], priv2) 873 874 def test_address_to_scripthash(self): 875 for priv_details in self.priv_pub_addr: 876 sh = address_to_scripthash(priv_details['address']) 877 self.assertEqual(priv_details['scripthash'], sh) 878 879 def test_is_minikey(self): 880 for priv_details in self.priv_pub_addr: 881 minikey = priv_details['minikey'] 882 priv = priv_details['priv'] 883 self.assertEqual(minikey, is_minikey(priv)) 884 885 def test_is_compressed_privkey(self): 886 for priv_details in self.priv_pub_addr: 887 self.assertEqual(priv_details['compressed'], 888 is_compressed_privkey(priv_details['priv'])) 889 890 def test_segwit_uncompressed_pubkey(self): 891 with self.assertRaises(BitcoinException): 892 is_private_key("p2wpkh-p2sh:5JKXxT3wAZHcybJ9YNkuHur9vou6uuAnorBV9A8vVxGNFH5wvTW", 893 raise_on_error=True) 894 895 def test_wif_with_invalid_magic_byte_for_compressed_pubkey(self): 896 with self.assertRaises(BitcoinException): 897 is_private_key("KwFAa6AumokBD2dVqQLPou42jHiVsvThY1n25HJ8Ji8REf1wxAQb", 898 raise_on_error=True) 899 900 901 class TestBaseEncode(ElectrumTestCase): 902 903 def test_base43(self): 904 tx_hex = "020000000001021cd0e96f9ca202e017ca3465e3c13373c0df3a4cdd91c1fd02ea42a1a65d2a410000000000fdffffff757da7cf8322e5063785e2d8ada74702d2648fa2add2d533ba83c52eb110df690200000000fdffffff02d07e010000000000160014b544c86eaf95e3bb3b6d2cabb12ab40fc59cad9ca086010000000000232102ce0d066fbfcf150a5a1bbc4f312cd2eb080e8d8a47e5f2ce1a63b23215e54fb5ac02483045022100a9856bf10a950810abceeabc9a86e6ba533e130686e3d7863971b9377e7c658a0220288a69ef2b958a7c2ecfa376841d4a13817ed24fa9a0e0a6b9cb48e6439794c701210324e291735f83ff8de47301b12034950b80fa4724926a34d67e413d8ff8817c53024830450221008f885978f7af746679200ed55fe2e86c1303620824721f95cc41eb7965a3dfcf02207872082ac4a3c433d41a203e6d685a459e70e551904904711626ac899238c20a0121023d4c9deae1aacf3f822dd97a28deaec7d4e4ff97be746d124a63d20e582f5b290a971600" 905 tx_bytes = bfh(tx_hex) 906 tx_base43 = base_encode(tx_bytes, base=43) 907 self.assertEqual("3E2DH7.J3PKVZJ3RCOXQVS3Y./6-WE.75DDU0K58-0N1FRL565N8ZH-DG1Z.1IGWTE5HK8F7PWH5P8+V3XGZZ6GQBPHNDE+RD8CAQVV1/6PQEMJIZTGPMIJ93B8P$QX+Y2R:TGT9QW8S89U4N2.+FUT8VG+34USI/N/JJ3CE*KLSW:REE8T5Y*9:U6515JIUR$6TODLYHSDE3B5DAF:5TF7V*VAL3G40WBOM0DO2+CFKTTM$G-SO:8U0EW:M8V:4*R9ZDX$B1IRBP9PLMDK8H801PNTFB4$HL1+/U3F61P$4N:UAO88:N5D+J:HI4YR8IM:3A7K1YZ9VMRC/47$6GGW5JEL1N690TDQ4XW+TWHD:V.1.630QK*JN/.EITVU80YS3.8LWKO:2STLWZAVHUXFHQ..NZ0:.J/FTZM.KYDXIE1VBY7/:PHZMQ$.JZQ2.XT32440X/HM+UY/7QP4I+HTD9.DUSY-8R6HDR-B8/PF2NP7I2-MRW9VPW3U9.S0LQ.*221F8KVMD5ANJXZJ8WV4UFZ4R.$-NXVE+-FAL:WFERGU+WHJTHAP", 908 tx_base43) 909 self.assertEqual(tx_bytes, 910 base_decode(tx_base43, base=43)) 911 912 def test_base58(self): 913 data_hex = '0cd394bef396200774544c58a5be0189f3ceb6a41c8da023b099ce547dd4d8071ed6ed647259fba8c26382edbf5165dfd2404e7a8885d88437db16947a116e451a5d1325e3fd075f9d370120d2ab537af69f32e74fc0ba53aaaa637752964b3ac95cfea7' 914 data_bytes = bfh(data_hex) 915 data_base58 = base_encode(data_bytes, base=58) 916 self.assertEqual("VuvZ2K5UEcXCVcogny7NH4Evd9UfeYipsTdWuU4jLDhyaESijKtrGWZTFzVZJPjaoC9jFBs3SFtarhDhQhAxkXosUD8PmUb5UXW1tafcoPiCp8jHy7Fe2CUPXAbYuMvAyrkocbe6", 917 data_base58) 918 self.assertEqual(data_bytes, 919 base_decode(data_base58, base=58)) 920 921 def test_base58check(self): 922 data_hex = '0cd394bef396200774544c58a5be0189f3ceb6a41c8da023b099ce547dd4d8071ed6ed647259fba8c26382edbf5165dfd2404e7a8885d88437db16947a116e451a5d1325e3fd075f9d370120d2ab537af69f32e74fc0ba53aaaa637752964b3ac95cfea7' 923 data_bytes = bfh(data_hex) 924 data_base58check = EncodeBase58Check(data_bytes) 925 self.assertEqual("4GCCJsjHqFbHxWbFBvRg35cSeNLHKeNqkXqFHW87zRmz6iP1dJU9Tk2KHZkoKj45jzVsSV4ZbQ8GpPwko6V3Z7cRfux3zJhUw7TZB6Kpa8Vdya8cMuUtL5Ry3CLtMetaY42u52X7Ey6MAH", 926 data_base58check) 927 self.assertEqual(data_bytes, 928 DecodeBase58Check(data_base58check))