def test_from_invalid_pubkeys(self): """Create P2PKHBitcoinAddress's from invalid pubkeys""" # first test with accept_invalid=True def T(invalid_pubkey, expected_str_addr): addr = P2PKHBitcoinAddress.from_pubkey(invalid_pubkey, accept_invalid=True) self.assertEqual(str(addr), expected_str_addr) T(x(''), legacy_to_cashaddr('1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E')) T( x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72' ), legacy_to_cashaddr('1L9V4NXbNtZsLjrD3nkU7gtEYLWRBWXLiZ')) # With accept_invalid=False we should get CBitcoinAddressError's with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_pubkey(x('')) with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_pubkey( x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72' )) with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_pubkey( CPubKey( x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72' )))
def T(serialized1, serialized2, are_equal): script1 = CScript(x(serialized1)) script2 = CScript(x(serialized2)) if are_equal: self.assertEqual(script1, script2) else: self.assertNotEqual(script1, script2)
def test_from_nonstd_scriptPubKey(self): """CBitcoinAddress.from_scriptPubKey() with non-standard scriptPubKeys""" # Bad P2SH scriptPubKeys # non-canonical pushdata scriptPubKey = CScript( x('a94c14000000000000000000000000000000000000000087')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey) # Bad P2PKH scriptPubKeys # Missing a byte scriptPubKey = CScript( x('76a914000000000000000000000000000000000000000088')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey) # One extra byte scriptPubKey = CScript( x('76a914000000000000000000000000000000000000000088acac')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey) # One byte changed scriptPubKey = CScript( x('76a914000000000000000000000000000000000000000088ad')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey)
def test_bloom_create_insert_key(self): filter = CBloomFilter(2, 0.001, 0, CBloomFilter.UPDATE_ALL) pubkey = x( '045B81F0017E2091E2EDCD5EECF10D5BDD120A5514CB3EE65B8447EC18BFC4575C6D5BF415E54E03B1067934A0F0BA76B01C6B9AB227142EE1D543764B69D901E0' ) pubkeyhash = bitcoincash.core.Hash160(pubkey) filter.insert(pubkey) filter.insert(pubkeyhash) self.assertEqual(filter.serialize(), x('038fc16b080000000000000001'))
def test_create_from_string(self): """Create CBitcoinAddress's from strings""" def T(str_addr, expected_bytes, expected_type, expected_class): addr = CBitcoinAddress(str_addr) self.assertEqual(addr.to_bytes(), expected_bytes) self.assertEqual(addr.kind, expected_type) self.assertEqual(addr.__class__, expected_class) T('bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy', x('62e907b15cbf27d5425399ebf6f0fb50ebb88f18'), bitcoincash.cashaddr.PUBKEY_TYPE, P2PKHBitcoinAddress) T('bitcoincash:pppxdlr09s5xr4l7y2dj0xnesqa0efa6xsxkp8ax88', x('4266fc6f2c2861d7fe229b279a79803afca7ba34'), bitcoincash.cashaddr.SCRIPT_TYPE, P2SHBitcoinAddress)
def test_from_invalid_scriptPubKey(self): """CBitcoinAddress.from_scriptPubKey() with invalid scriptPubKeys""" # We should raise a CBitcoinAddressError, not any other type of error # Truncated P2SH scriptPubKey = CScript(x('a91400000000000000000000000000000000000000')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey) # Truncated P2PKH scriptPubKey = CScript( x('76a91400000000000000000000000000000000000000')) with self.assertRaises(CBitcoinAddressError): CBitcoinAddress.from_scriptPubKey(scriptPubKey)
def fundrawtransaction(self, given_transaction, *args, **kwargs): """ Make up some inputs for the given transaction. """ # just use any txid here vintxid = lx("99264749804159db1e342a0c8aa3279f6ef4031872051a1e52fb302e51061bef") if isinstance(given_transaction, str): given_bytes = x(given_transaction) elif isinstance(given_transaction, CMutableTransaction): given_bytes = given_transaction.serialize() else: raise FakeBitcoinProxyException("Wrong type passed to fundrawtransaction.") # this is also a clever way to not cause a side-effect in this function transaction = CMutableTransaction.deserialize(given_bytes) for vout_counter in range(0, self._num_fundrawtransaction_inputs): txin = CMutableTxIn(COutPoint(vintxid, vout_counter)) transaction.vin.append(txin) # also allocate a single output (for change) txout = make_txout() transaction.vout.append(txout) transaction_hex = b2x(transaction.serialize()) return {"hex": transaction_hex, "fee": 5000000}
def getblockheader(self, block_hash, verbose=False): """Get block header <block_hash> verbose - If true a dict is returned with the values returned by getblockheader that are not in the block header itself (height, nextblockhash, etc.) Raises IndexError if block_hash is not valid. """ try: block_hash = b2lx(block_hash) except TypeError: raise TypeError( '%s.getblockheader(): block_hash must be bytes; got %r instance' % (self.__class__.__name__, block_hash.__class__)) try: r = self._call('getblockheader', block_hash, verbose) except InvalidAddressOrKeyError as ex: raise IndexError('%s.getblockheader(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) if verbose: nextblockhash = None if 'nextblockhash' in r: nextblockhash = lx(r['nextblockhash']) return { 'confirmations': r['confirmations'], 'height': r['height'], 'mediantime': r['mediantime'], 'nextblockhash': nextblockhash, 'chainwork': x(r['chainwork']) } else: return CBlockHeader.deserialize(unhexlify(r))
def test_from_bare_checksig_scriptPubKey(self): def T(hex_scriptpubkey, expected_str_address): scriptPubKey = CScript(x(hex_scriptpubkey)) addr = P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) self.assertEqual(str(addr), expected_str_address) # now test that CBitcoinAddressError is raised with accept_non_canonical_pushdata=False with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_scriptPubKey( scriptPubKey, accept_bare_checksig=False) # compressed T( '21000000000000000000000000000000000000000000000000000000000000000000ac', legacy_to_cashaddr('14p5cGy5DZmtNMQwTQiytBvxMVuTmFMSyU')) # uncompressed T( '410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac', legacy_to_cashaddr('1QLFaVVt99p1y18zWSZnespzhkFxjwBbdP')) # non-canonical encoding T( '4c21000000000000000000000000000000000000000000000000000000000000000000ac', legacy_to_cashaddr('14p5cGy5DZmtNMQwTQiytBvxMVuTmFMSyU')) # odd-lengths are *not* accepted with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_scriptPubKey( x('2200000000000000000000000000000000000000000000000000000000000000000000ac' ))
def T(serialized_script, expected_tokens, test_roundtrip=True): serialized_script = x(serialized_script) script_obj = CScript(serialized_script) actual_tokens = list(script_obj) self.assertEqual(actual_tokens, expected_tokens) if test_roundtrip: recreated_script = CScript(actual_tokens) self.assertEqual(recreated_script, serialized_script)
def T(hex_scriptpubkey, expected_str_address): scriptPubKey = CScript(x(hex_scriptpubkey)) addr = P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) self.assertEqual(str(addr), expected_str_address) # now test that CBitcoinAddressError is raised with accept_non_canonical_pushdata=False with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_scriptPubKey( scriptPubKey, accept_bare_checksig=False)
async def sync_headers(cli, headers, tip_height): for h in range(START_HEIGHT, tip_height + 1): # TODO: Discover reorgs if h in headers: continue header_hex = await cli.RPC('blockchain.block.header', h) headers[h] = CBlockHeader.deserialize(x(header_hex)) print("Fetched header {}".format(h))
def test_from_valid_pubkey(self): """Create P2PKHBitcoinAddress's from valid pubkeys""" def T(pubkey, expected_str_addr): addr = P2PKHBitcoinAddress.from_pubkey(pubkey) self.assertEqual(str(addr), legacy_to_cashaddr(expected_str_addr)) T( x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71' ), '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') T( x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455' ), '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T') T( CPubKey( x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71' )), '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') T( CPubKey( x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455' )), '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T')
async def sync_coinbases(cli, coinbases, tip_height): for h in range(START_HEIGHT, tip_height + 1): # TODO: Discover reorgs if h in coinbases: continue coinbase_txid = await cli.RPC('blockchain.transaction.id_from_pos', h, 0) tx_raw = await cli.RPC('blockchain.transaction.get', coinbase_txid) print(f"Fetched coinbase {h} {coinbase_txid}") coinbases[h] = CTransaction.deserialize(x(tx_raw))
def test_repr(self): def T(script, expected_repr): actual_repr = repr(script) self.assertEqual(actual_repr, expected_repr) T(CScript([]), 'CScript([])') T(CScript([1]), 'CScript([1])') T(CScript([1, 2, 3]), 'CScript([1, 2, 3])') T( CScript( [1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP]), "CScript([1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP])" ) T(CScript(x('0001ff515261ff')), "CScript([0, x('ff'), 1, 2, OP_NOP, OP_INVALIDOPCODE])") # truncated scripts T(CScript(x('6101')), "CScript([OP_NOP, x('')...<ERROR: PUSHDATA(1): truncated data>])") T( CScript(x('614bff')), "CScript([OP_NOP, x('ff')...<ERROR: PUSHDATA(75): truncated data>])" ) T(CScript(x('614c')), "CScript([OP_NOP, <ERROR: PUSHDATA1: missing data length>])") T(CScript(x('614c0200')), "CScript([OP_NOP, x('00')...<ERROR: PUSHDATA1: truncated data>])")
def test_create_insert_serialize(self): filter = CBloomFilter(3, 0.01, 0, CBloomFilter.UPDATE_ALL) def T(elem): """Filter contains elem""" elem = x(elem) filter.insert(elem) self.assertTrue(filter.contains(elem)) def F(elem): """Filter does not contain elem""" elem = x(elem) self.assertFalse(filter.contains(elem)) T('99108ad8ed9bb6274d3980bab5a85c048f0950c8') F('19108ad8ed9bb6274d3980bab5a85c048f0950c8') T('b5a2c786d9ef4658287ced5914b37a1b4aa32eee') T('b9300670b4c5366e95b2699e8b18bc75e5f729c5') self.assertEqual(filter.serialize(), x('03614e9b050000000000000001')) deserialized = CBloomFilter.deserialize( x('03614e9b050000000000000001')) self.assertTrue( deserialized.contains( x('99108ad8ed9bb6274d3980bab5a85c048f0950c8'))) self.assertFalse( deserialized.contains( x('19108ad8ed9bb6274d3980bab5a85c048f0950c8'))) self.assertTrue( deserialized.contains( x('b5a2c786d9ef4658287ced5914b37a1b4aa32eee'))) self.assertTrue( deserialized.contains( x('b9300670b4c5366e95b2699e8b18bc75e5f729c5')))
def test_fundrawtransaction_adds_output(self): num_outputs = 5 unfunded_transaction = CMutableTransaction( [], [make_txout() for x in range(0, num_outputs)]) proxy = FakeBitcoinProxy() funded_transaction_hex = proxy.fundrawtransaction( b2x(unfunded_transaction.serialize()))["hex"] funded_transaction = CMutableTransaction.deserialize( x(funded_transaction_hex)) self.assertTrue(len(funded_transaction.vout) > num_outputs) self.assertEqual(len(funded_transaction.vout), num_outputs + 1)
def sendrawtransaction(self, given_transaction): """ Pretend to broadcast and relay the transaction. Return the txid of the given transaction. """ if isinstance(given_transaction, str): given_bytes = x(given_transaction) elif isinstance(given_transaction, CMutableTransaction): given_bytes = given_transaction.serialize() else: raise FakeBitcoinProxyException("Wrong type passed to sendrawtransaction.") transaction = CMutableTransaction.deserialize(given_bytes) return b2lx(transaction.GetHash())
def signrawtransaction(self, given_transaction): """ This method does not actually sign the transaction, but it does return a transaction based on the given transaction. """ if isinstance(given_transaction, str): given_bytes = x(given_transaction) elif isinstance(given_transaction, CMutableTransaction): given_bytes = given_transaction.serialize() else: raise FakeBitcoinProxyException("Wrong type passed to signrawtransaction.") transaction = CMutableTransaction.deserialize(given_bytes) transaction_hex = b2x(transaction.serialize()) return {"hex": transaction_hex}
def test_basic_rpc(self): skip_unless_enabled() loop = asyncio.get_event_loop() cli = bitcoincash.electrum.Electrum(loop=loop) loop.run_until_complete(cli.connect()) header_hex = loop.run_until_complete( cli.RPC('blockchain.block.header', 613880)) header = CBlockHeader.deserialize(x(header_hex)) self.assertEqual( "f9acfe3f0e98fc321123f787e21e17984998f46866d30244328bf7552051d57a", b2lx(header.hashMerkleRoot)) loop.run_until_complete(cli.close()) loop.close()
def test_fundrawtransaction_hex_hash(self): unfunded_transaction = CMutableTransaction( [], [make_txout() for x in range(0, 5)]) proxy = FakeBitcoinProxy() funded_transaction_hex = proxy.fundrawtransaction( b2x(unfunded_transaction.serialize()))["hex"] funded_transaction = CMutableTransaction.deserialize( x(funded_transaction_hex)) self.assertTrue(unfunded_transaction is not funded_transaction) self.assertEqual(type(funded_transaction), type(unfunded_transaction)) self.assertNotEqual(len(funded_transaction.vin), 0) self.assertTrue( len(funded_transaction.vin) > len(unfunded_transaction.vin)) self.assertEqual(type(funded_transaction.vin[0]), CTxIn)
def test_to_p2sh_scriptPubKey(self): def T(redeemScript, expected_hex_bytes): redeemScript = CScript(redeemScript) actual_script = redeemScript.to_p2sh_scriptPubKey() self.assertEqual(b2x(actual_script), expected_hex_bytes) T([], 'a914b472a266d0bd89c13706a4132ccfb16f7c3b9fcb87') T([ 1, x('029b6d2c97b8b7c718c325d7be3ac30f7c9d67651bce0c929f55ee77ce58efcf84' ), 1, OP_CHECKMULTISIG ], 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87') T([b'\xff' * 517], 'a9140da7fa40ebf248dfbca363c79921bdd665fed5ba87') with self.assertRaises(ValueError): CScript([b'a' * 518]).to_p2sh_scriptPubKey()
def test_create_insert_serialize_with_tweak(self): # Same test as bloom_create_insert_serialize, but we add a nTweak of 100 filter = CBloomFilter(3, 0.01, 2147483649, CBloomFilter.UPDATE_ALL) def T(elem): """Filter contains elem""" elem = x(elem) filter.insert(elem) self.assertTrue(filter.contains(elem)) def F(elem): """Filter does not contain elem""" elem = x(elem) self.assertFalse(filter.contains(elem)) T('99108ad8ed9bb6274d3980bab5a85c048f0950c8') F('19108ad8ed9bb6274d3980bab5a85c048f0950c8') T('b5a2c786d9ef4658287ced5914b37a1b4aa32eee') T('b9300670b4c5366e95b2699e8b18bc75e5f729c5') self.assertEqual(filter.serialize(), x('03ce4299050000000100008001'))
def test_from_non_canonical_scriptPubKey(self): def T(hex_scriptpubkey, expected_str_address): scriptPubKey = CScript(x(hex_scriptpubkey)) addr = P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) self.assertEqual(str(addr), expected_str_address) # now test that CBitcoinAddressError is raised with accept_non_canonical_pushdata=False with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_scriptPubKey( scriptPubKey, accept_non_canonical_pushdata=False) T('76a94c14000000000000000000000000000000000000000088ac', legacy_to_cashaddr('1111111111111111111114oLvT2')) T('76a94d1400000000000000000000000000000000000000000088ac', legacy_to_cashaddr('1111111111111111111114oLvT2')) T('76a94e14000000000000000000000000000000000000000000000088ac', legacy_to_cashaddr('1111111111111111111114oLvT2')) # make sure invalid scripts raise CBitcoinAddressError with self.assertRaises(CBitcoinAddressError): P2PKHBitcoinAddress.from_scriptPubKey(x('76a94c14'))
def test_signrawtransaction(self): num_outputs = 5 given_transaction = CMutableTransaction( [], [make_txout() for x in range(0, num_outputs)]) proxy = FakeBitcoinProxy() result = proxy.signrawtransaction(given_transaction) self.assertEqual(type(result), dict) self.assertTrue("hex" in result.keys()) result_transaction_hex = result["hex"] result_transaction = CMutableTransaction.deserialize( x(result_transaction_hex)) self.assertTrue(result_transaction is not given_transaction) self.assertTrue(result_transaction.vin is not given_transaction.vin) self.assertTrue(result_transaction.vout is not given_transaction.vout) self.assertEqual(len(result_transaction.vout), len(given_transaction.vout)) self.assertEqual(result_transaction.vout[0].scriptPubKey, given_transaction.vout[0].scriptPubKey)
def T(hex_scriptpubkey, expected_str_address, expected_class): scriptPubKey = CScript(x(hex_scriptpubkey)) addr = CBitcoinAddress.from_scriptPubKey(scriptPubKey) self.assertEqual(str(addr), expected_str_address) self.assertEqual(addr.__class__, expected_class)
def test(self): if not is_libsec256k1_available(): return use_libsecp256k1_for_signing(True) # Test Vectors for RFC 6979 ECDSA, secp256k1, SHA-256 # (private key, message, expected k, expected signature) test_vectors = [ (0x1, "Satoshi Nakamoto", 0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5" ), (0x1, "All those moments will be lost in time, like tears in rain. Time to die...", 0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21" ), (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, "Satoshi Nakamoto", 0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5" ), (0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, "Alan Turing", 0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea" ), (0xe91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2, "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", 0x1F4B84C23A86A221D233F2521BE018D9318639D5B8BBD6374A8A59232D16AD3D, "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6" ) ] for vector in test_vectors: secret = CBitcoinSecret.from_secret_bytes( x('{:064x}'.format(vector[0]))) encoded_sig = secret.signECDSA( hashlib.sha256(vector[1].encode('utf8')).digest()) assert (encoded_sig[0] == 0x30) assert (encoded_sig[1] == len(encoded_sig) - 2) assert (encoded_sig[2] == 0x02) rlen = encoded_sig[3] rpos = 4 assert (rlen in (32, 33)) if rlen == 33: assert (encoded_sig[rpos] == 0) rpos += 1 rlen -= 1 rval = encoded_sig[rpos:rpos + rlen] spos = rpos + rlen assert (encoded_sig[spos] == 0x02) spos += 1 slen = encoded_sig[spos] assert (slen in (32, 33)) spos += 1 if slen == 33: assert (encoded_sig[spos] == 0) spos += 1 slen -= 1 sval = encoded_sig[spos:spos + slen] sig = b2x(rval + sval) assert (str(sig) == vector[3]) use_libsecp256k1_for_signing(False)
def F(elem): """Filter does not contain elem""" elem = x(elem) self.assertFalse(filter.contains(elem))
def T(hex_pubkey, is_valid, is_fullyvalid, is_compressed): key = CPubKey(x(hex_pubkey)) self.assertEqual(key.is_valid, is_valid) self.assertEqual(key.is_fullyvalid, is_fullyvalid) self.assertEqual(key.is_compressed, is_compressed)
def T(elem): """Filter contains elem""" elem = x(elem) filter.insert(elem) self.assertTrue(filter.contains(elem))