Ejemplo n.º 1
0
 def test_load(self):
     filename = 'mainnet.blocks.tmp'
     if exists(filename):
         unlink(filename)
     height = 4032
     node = SimpleNode(host='btcd0.lightning.computer')
     cs = BlockStore(node=node, filename=filename, full=True)
     cs.update(height)
     self.assertEqual(cs.store_height(), height)
     cs.save()
     new_cs = BlockStore(node=node, filename=filename, full=True)
     self.assertEqual(new_cs.store_height(), height)
     height = 6048
     new_cs.update(height)
     new_cs.save()
     new_cs.save()
     self.assertEqual(new_cs.store_height(), height)
     new_cs = BlockStore(node=node, filename=filename)
     self.assertEqual(new_cs.store_height(), height)
     self.assertEqual(len(new_cs.headers), 2017)
     height = 6148
     new_cs.update(height)
     self.assertEqual(new_cs.store_height(), height)
     new_cs.save()
     unlink(filename)
     filename = 'testnet.blocks.tmp'
     if exists(filename):
         unlink(filename)
     height = 4032
     node = SimpleNode(host='programmingblockchain.com', testnet=True)
     cs = BlockStore(node=node, filename=filename, full=True)
     cs.update(height)
     self.assertEqual(len(cs.headers), height + 1)
     cs.save()
     unlink(filename)
Ejemplo n.º 2
0
 def test_exercise_2(self):
     block_hash = bytes.fromhex(
         '0000000053787814ed9dd8c029d0a0a9af4ab8ec0591dc31bdc4ab31fae88ce9')
     passphrase = b'Jimmy Song Programming Blockchain'  # FILL THIS IN
     secret = little_endian_to_int(hash256(passphrase))
     private_key = PrivateKey(secret=secret)
     addr = private_key.point.address(testnet=True)
     filter_size = 30
     filter_num_functions = 5
     filter_tweak = 90210  # FILL THIS IN
     h160 = decode_base58(addr)
     bf = BloomFilter(filter_size, filter_num_functions, filter_tweak)
     bf.add(h160)
     node = SimpleNode('tbtc.programmingblockchain.com',
                       testnet=True,
                       logging=False)
     node.handshake()
     node.send(bf.filterload())
     getdata = GetDataMessage()
     getdata.add_data(FILTERED_BLOCK_DATA_TYPE, block_hash)
     node.send(getdata)
     mb = node.wait_for(MerkleBlock)
     tx = node.wait_for(Tx)
     self.assertEqual(
         tx.serialize().hex(),
         '0100000002a663815ab2b2ba5f53e442f9a2ea6cc11bbcd98fb1585e48a134bd870dbfbd6a000000006a47304402202151107dc2367cf5a9e2429cde0641c252374501214ce52069fbca1320180aa602201a43b5d4f91e48514c00c01521dc04f02c57f15305adc4eaad01c418f6e7a1180121031dbe3aff7b9ad64e2612b8b15e9f5e4a3130663a526df91abfb7b1bd16de5d6effffffff618b00a343488bd62751cf21f368ce3be76e3a0323fdc594a0d24f27a1155cd2000000006a473044022024c4dd043ab8637c019528b549e0b10333b2dfa83e7ca66776e401ad3fc31b6702207d4d1d73ac8940c59c57c0b7daf084953324154811c10d06d0563947a88f99b20121031dbe3aff7b9ad64e2612b8b15e9f5e4a3130663a526df91abfb7b1bd16de5d6effffffff0280969800000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788aca0ce6594000000001976a9146e13971913b9aa89659a9f53d327baa8826f2d7588ac00000000'
     )
Ejemplo n.º 3
0
 def test_example_1(self):
     node = SimpleNode('tbtc.programmingblockchain.com',
                       testnet=True,
                       logging=False)
     version = VersionMessage()
     node.send(version)
     verack = node.wait_for(VerAckMessage)
     self.assertEqual(verack.command, b'verack')
Ejemplo n.º 4
0
 def __init__(self, encrypted_private, public, next_external, next_internal,
              creation_height, sync_height, utxos, stxos, filename):
     self.encrypted_private = encrypted_private
     self.public = public
     self.next_external = next_external
     self.next_internal = next_internal
     self.creation_height = creation_height
     self.sync_height = sync_height
     self.utxo_lookup = {(utxo.txo.tx_id, utxo.txo.tx_index): utxo
                         for utxo in utxos}
     self.stxo_lookup = {(stxo.spending_tx_id, stxo.spending_tx_index): stxo
                         for stxo in stxos}
     self.filename = filename
     self.testnet = self.public.testnet
     self.tx_store = TxStore.get_store(testnet=self.testnet)
     self.node = SimpleNode(host='192.168.1.200', testnet=self.testnet)
     self.block_store = BlockStore(node=self.node, include=self.sync_height)
     sleep(1)
     if not self.creation_height:
         # this is a new wallet
         self.creation_height = self.sync_height = self.node.latest_block
Ejemplo n.º 5
0
 def test_exercise_10(self):
     block_hex = '0000000000044b01a9440b34f582fe171c7b8642fedd0ebfccf8fdf6a1810900'
     block_hash = bytes.fromhex(block_hex)
     node = SimpleNode('tbtc.programmingblockchain.com', testnet=True)
     node.handshake()
     getdata = GetDataMessage()
     getdata.add_data(BLOCK_DATA_TYPE, block_hash)
     node.send(getdata)
     b = node.wait_for(Block)
     self.assertTrue(b.check_pow())
     self.assertTrue(b.validate_merkle_root())
Ejemplo n.º 6
0
def connect_bitcoin(host, port, tor=False, logging=False, process_name="main"):
    if tor:
        if logging:
            logging.info("[-] Tor connection is requested")
            logging.info("[-] Connecting to bitcoin node {}:{}".format(host, port))
        node = SimpleNode(host, port, logging=log_info, tor=tor, process_name=process_name)
        if node.tor:
            if logging:
                logging.info("[-] SOCKS5 proxy listener on TCP:{}".format(node.socks_port))
            node.handshake()
    else:
        if logging:
            logging.info("[-] Connecting to bitcoin node {}:{}".format(host, port))
        node = SimpleNode(host, port, logging=log_info, tor=tor)
        node.handshake()
    return node
Ejemplo n.º 7
0
 def test_exercise_10(self):
     block_hex = '0000000000044b01a9440b34f582fe171c7b8642fedd0ebfccf8fdf6a1810900'
     block_hash = bytes.fromhex(block_hex)
     node = SimpleNode('tbtc.programmingblockchain.com', testnet=True)
     node.handshake()
     getdata = GetDataMessage()
     getdata.add_data(BLOCK_DATA_TYPE, block_hash)
     node.send(getdata.command, getdata.serialize())
     block_envelope = node.wait_for_commands([b'block'])
     stream = block_envelope.stream()
     b = Block.parse(stream)
     self.assertTrue(b.check_pow())
     num_txs = read_varint(stream)
     tx_hashes = []
     for _ in range(num_txs):
         t = Tx.parse(stream)
         tx_hashes.append(t.hash())
     b.tx_hashes = tx_hashes
     self.assertTrue(b.validate_merkle_root())
Ejemplo n.º 8
0
 def test_exercise_5(self):
     expected = [
         '00000000864b744c5025331036aa4a16e9ed1cbb362908c625272150fa059b29',
         '000000002e9ccffc999166ccf8d72129e1b2e9c754f6c90ad2f77cab0d9fb4c7',
         '0000000009b9f0436a9c733e2c9a9d9c8fe3475d383bdc1beb7bfa995f90be70',
         '000000000a9c9c79f246042b9e2819822287f2be7cd6487aecf7afab6a88bed5',
         '000000003a7002e1247b0008cba36cd46f57cd7ce56ac9d9dc5644265064df09',
         '00000000061e01e82afff6e7aaea4eb841b78cc0eed3af11f6706b14471fa9c8',
         '000000003911e011ae2459e44d4581ac69ba703fb26e1421529bd326c538f12d',
         '000000000a5984d6c73396fe40de392935f5fc2a8e48eedf38034ce0a3178a60',
         '000000000786bdc642fa54c0a791d58b732ed5676516fffaeca04492be97c243',
         '000000001359c49f9618f3ee69afbd1b3196f1832acc47557d42256fcc6b7f48',
         '00000000270dde98d582af35dff5aed02087dad8529dc5c808c67573d6dabaf4',
         '00000000425c160908c215c4adf998771a2d1c472051bc58320696f3a5eb0644',
         '0000000006a5976471986377805d4a148d8822bb7f458138c83f167d197817c9',
         '000000000318394ea17038ef369f3cccc79b3d7dfda957af6c8cd4a471ffa814',
         '000000000ad4f9d0b8e86871478cc849f7bc42fb108ebec50e4a795afc284926',
         '000000000207e63e68f2a7a4c067135883d726fd65e3620142fb9bdf50cce1f6',
         '00000000003b426d2c12ee66b2eedb4dcc05d5e158685b222240d31e43687762',
         '00000000017cf6ee86e3d483f9a978ded72be1fa5af37d287a71c5dfb87cdd83',
         '00000000004b1d9fe16fc0c72cfa0395c98a3e460cd2affb8640e28bca295a4a'
     ]
     node = SimpleNode('tbtc.programmingblockchain.com', testnet=True)
     node.handshake()
     last_block_hash = TESTNET_GENESIS_BLOCK_HASH
     count = 1
     while count <= 40000:
         getheaders = GetHeadersMessage(start_block=last_block_hash)
         node.send(getheaders.command, getheaders.serialize())
         headers_envelope = node.wait_for_commands([b'headers'])
         headers_message = HeadersMessage.parse(headers_envelope.stream())
         for b in headers_message.blocks:
             self.assertTrue(b.check_pow())
             if last_block_hash != TESTNET_GENESIS_BLOCK_HASH:
                 print(count)
                 self.assertEqual(b.prev_block, last_block_hash)
             count += 1
             last_block_hash = b.hash()
             if count % 2016 == 0:
                 self.assertEqual(b.id(), expected.pop(0))
Ejemplo n.º 9
0
 def test_example_2(self):
     expected = [
         '00000000693067b0e6b440bc51450b9f3850561b07f6d3c021c54fbd6abb9763',
         '00000000f037ad09d0b05ee66b8c1da83030abaf909d2b1bf519c3c7d2cd3fdf',
         '000000006ce8b5f16fcedde13acbc9641baa1c67734f177d770a4069c06c9de8',
         '00000000563298de120522b5ae17da21aaae02eee2d7fcb5be65d9224dbd601c',
         '000000009b0a4b2833b4a0aa61171ee75b8eb301ac45a18713795a72e461a946',
         '00000000fa8a7363e8f6fdc88ec55edf264c9c7b31268c26e497a4587c750584',
         '000000008ac55b5cd76a5c176f2457f0e9df5ff1c719d939f1022712b1ba2092',
         '000000007f0c796631f00f542c0b402d638d3518bc208f8c9e5d29d2f169c084',
         '00000000ffb062296c9d4eb5f87bbf905d30669d26eab6bced341bd3f1dba5fd',
         '0000000074c108842c3ec2252bba62db4050bf0dddfee3ddaa5f847076b8822f',
         '0000000067dc2f84a73fbf5d3c70678ce4a1496ef3a62c557bc79cbdd1d49f22',
         '00000000dbf06f47c0624262ecb197bccf6bdaaabc2d973708ac401ac8955acc',
         '000000009260fe30ec89ef367122f429dcc59f61735760f2b2288f2e854f04ac',
         '00000000f9f1a700898c4e0671af6efd441eaf339ba075a5c5c7b0949473c80b',
         '000000005107662c86452e7365f32f8ffdc70d8d87aa6f78630a79f7d77fbfe6',
         '00000000984f962134a7291e3693075ae03e521f0ee33378ec30a334d860034b',
         '000000005e36047e39452a7beaaa6721048ac408a3e75bb60a8b0008713653ce',
         '00000000128d789579ffbec00203a371cbb39cee27df35d951fd66e62ed59258',
         '000000008dde642fb80481bb5e1671cb04c6716de5b7f783aa3388456d5c8a85'
     ]
     node = SimpleNode('btc.programmingblockchain.com', testnet=False)
     node.handshake()
     last_block_hash = GENESIS_BLOCK_HASH
     count = 1
     for _ in range(20):
         getheaders = GetHeadersMessage(start_block=last_block_hash)
         node.send(getheaders.command, getheaders.serialize())
         headers_envelope = node.wait_for_commands([b'headers'])
         headers_message = HeadersMessage.parse(headers_envelope.stream())
         for b in headers_message.blocks:
             self.assertTrue(b.check_pow())
             if last_block_hash != GENESIS_BLOCK_HASH:
                 self.assertEqual(b.prev_block, last_block_hash)
             count += 1
             last_block_hash = b.hash()
             if count % 2016 == 0:
                 self.assertEqual(b.id(), expected.pop(0))
Ejemplo n.º 10
0
 def test_example_1(self):
     node = SimpleNode('btc.programmingblockchain.com', testnet=False)
     node.handshake()
     last_block_hash = GENESIS_BLOCK_HASH
     first_epoch_block = None
     expected_bits = None
     count = 1
     want = ('ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d', 'ffff001d',
             'ffff001d', '6ad8001d', '28c4001d')
     for bits in want:
         getheaders = GetHeadersMessage(start_block=last_block_hash)
         node.send(getheaders.command, getheaders.serialize())
         headers_envelope = node.wait_for_commands([b'headers'])
         headers_message = HeadersMessage.parse(headers_envelope.stream())
         for block in headers_message.blocks:
             if not block.check_pow():
                 raise RuntimeError(
                     'bad proof of work at block {}'.format(count))
             if last_block_hash != GENESIS_BLOCK_HASH and block.prev_block != last_block_hash:
                 raise RuntimeError(
                     'discontinuous block at {}'.format(count))
             if expected_bits and block.bits != expected_bits:
                 raise RuntimeError('bad bits at block {} {} vs {}'.format(
                     count, block.bits.hex(), expected_bits.hex()))
             if first_epoch_block and count % 2016 == 2015:
                 expected_bits = calculate_new_bits(
                     expected_bits,
                     block.timestamp - first_epoch_block.timestamp)
                 self.assertEqual(expected_bits.hex(), bits)
             elif first_epoch_block is None:
                 expected_bits = block.bits
             if count % 2016 == 0 or not first_epoch_block:
                 first_epoch_block = block
             count += 1
             last_block_hash = block.hash()
Ejemplo n.º 11
0
 def test_exercise_4(self):
     last_block_hex = '000000000d65610b5af03d73ed67704713c9b734d87cf4b970d39a0416dd80f9'
     last_block = bytes.fromhex(last_block_hex)
     secret = little_endian_to_int(
         hash256(b'Jimmy Song Programming Blockchain'))
     private_key = PrivateKey(secret=secret)
     addr = private_key.point.address(testnet=True)
     h160 = decode_base58(addr)
     target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
     self.assertEqual(addr, target_address)
     filter_size = 30
     filter_num_functions = 5
     filter_tweak = 90210  # FILL THIS IN
     target_h160 = decode_base58(target_address)
     target_script = p2pkh_script(target_h160)
     fee = 5000  # fee in satoshis
     node = SimpleNode('tbtc.programmingblockchain.com',
                       testnet=True,
                       logging=False)
     bf = BloomFilter(filter_size, filter_num_functions, filter_tweak)
     bf.add(h160)
     node.handshake()
     node.send(bf.filterload())
     getheaders_message = GetHeadersMessage(start_block=last_block)
     node.send(getheaders_message)
     headers = node.wait_for(HeadersMessage)
     get_data_message = GetDataMessage()
     for header in headers.headers:
         self.assertTrue(header.check_pow())
         if last_block is not None:
             self.assertEqual(header.prev_block, last_block)
         last_block = header.hash()
         get_data_message.add_data(FILTERED_BLOCK_DATA_TYPE, last_block)
     node.send(get_data_message)
     prev_tx = None
     while prev_tx is None:
         message = node.wait_for(MerkleBlock, Tx)
         if message.command == b'merkleblock':
             self.assertTrue(message.is_valid())
         else:
             message.testnet = True
             for i, tx_out in enumerate(message.tx_outs):
                 if tx_out.script_pubkey.address(testnet=True) == addr:
                     prev_tx = message.hash()
                     prev_index = i
                     prev_amount = tx_out.amount
                     break
     tx_in = TxIn(prev_tx, prev_index)
     output_amount = prev_amount - fee
     tx_out = TxOut(output_amount, target_script)
     tx_obj = Tx(1, [tx_in], [tx_out], 0, testnet=True)
     tx_obj.sign_input(0, private_key)
     self.assertEqual(
         tx_obj.serialize().hex(),
         '010000000194e631abb9e1079ec72a1616a3aa0111c614e65b96a6a4420e2cc6af9e6cc96e000000006a47304402203cc8c56abe1c0dd043afa9eb125dafbebdde2dd4cd7abf0fb1aae0667a22006e02203c95b74d0f0735bbf1b261d36e077515b6939fc088b9d7c1b7030a5e494596330121021cdd761c7eb1c90c0af0a5963e94bf0203176b4662778d32bd6d7ab5d8628b32ffffffff01f8829800000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000'
     )
Ejemplo n.º 12
0
# book showcase

#last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19'
#address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'

# our test

#last_block_hex = '0000000017e6fbd8931bce659d45d92040a4674950f2ae5416d0bf1a239641f9'
last_block_hex = '00000000970369111c044804ec0319792c9e1aa29f59a622c5d14b3544ae4eba'
#0000000017e6fbd8931bce659d45d92040a4674950f2ae5416d0bf1a239641f9
#last_block_hex = '0000000000000004fea90996fdf40772e2c2c76205a1fb57fae465194fdaffb9'
address = 'mvEg6eZ3sUApodedYQrkpEPMMALsr1K1k1'

h160 = decode_base58(address)
node = SimpleNode('testnet.programmingbitcoin.com',
                  testnet=True,
                  logging=False)
bf = BloomFilter(size=30, function_count=5, tweak=90210)
bf.add(h160)
node.handshake()
node.send(bf.filterload())
start_block = bytes.fromhex(last_block_hex)
getheaders = GetHeadersMessage(start_block=start_block)
node.send(getheaders)
print('ok2')
headers = node.wait_for(HeadersMessage)
print('ok3')
getdata = GetDataMessage()
for b in headers.blocks:
    if not b.check_pow():
        raise RuntimeError('proof of work is invalid')
Ejemplo n.º 13
0
 def test_handshake(self):
     node = SimpleNode('testnet.programmingbitcoin.com', testnet=True)
     node.handshake()
Ejemplo n.º 14
0
 def test_exercise_6(self):
     last_block_hex = '000000000d65610b5af03d73ed67704713c9b734d87cf4b970d39a0416dd80f9'
     secret = little_endian_to_int(
         hash256(b'Jimmy Song Programming Blockchain'))
     private_key = PrivateKey(secret=secret)
     addr = private_key.point.address(testnet=True)
     h160 = decode_base58(addr)
     target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
     self.assertEqual(addr, target_address)
     target_h160 = decode_base58(target_address)
     target_script = p2pkh_script(target_h160)
     fee = 5000
     node = SimpleNode('tbtc.programmingblockchain.com', testnet=True)
     bf = BloomFilter(30, 5, 90210)
     bf.add(h160)
     node.handshake()
     node.send(bf.filterload())
     start_block = bytes.fromhex(last_block_hex)
     getheaders = GetHeadersMessage(start_block=start_block)
     node.send(getheaders)
     headers = node.wait_for(HeadersMessage)
     last_block = None
     getdata = GetDataMessage()
     for b in headers.blocks:
         if not b.check_pow():
             raise RuntimeError('proof of work is invalid')
         if last_block is not None and b.prev_block != last_block:
             raise RuntimeError('chain broken')
         getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())
         last_block = b.hash()
     node.send(getdata)
     prev_tx, prev_index, prev_tx_obj = None, None, None
     while prev_tx is None:
         message = node.wait_for(MerkleBlock, Tx)
         if message.command == b'merkleblock':
             if not message.is_valid():
                 raise RuntimeError('invalid merkle proof')
         else:
             message.testnet = True
             for i, tx_out in enumerate(message.tx_outs):
                 if tx_out.script_pubkey.address(testnet=True) == addr:
                     prev_tx = message.hash()
                     prev_index = i
                     prev_amount = tx_out.amount
                     self.assertEqual(
                         message.id(),
                         '6ec96c9eafc62c0e42a4a6965be614c61101aaa316162ac79e07e1b9ab31e694'
                     )
                     self.assertEqual(i, 0)
                     break
     tx_in = TxIn(prev_tx, prev_index)
     output_amount = prev_amount - fee
     tx_out = TxOut(output_amount, target_script)
     tx_obj = Tx(1, [tx_in], [tx_out], 0, testnet=True)
     tx_obj.sign_input(0, private_key)
     self.assertEqual(
         tx_obj.serialize().hex(),
         '010000000194e631abb9e1079ec72a1616a3aa0111c614e65b96a6a4420e2cc6af9e6cc96e000000006a47304402203cc8c56abe1c0dd043afa9eb125dafbebdde2dd4cd7abf0fb1aae0667a22006e02203c95b74d0f0735bbf1b261d36e077515b6939fc088b9d7c1b7030a5e494596330121021cdd761c7eb1c90c0af0a5963e94bf0203176b4662778d32bd6d7ab5d8628b32ffffffff01f8829800000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000'
     )
Ejemplo n.º 15
0
 def test_example_5(self):
     last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19'
     address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
     h160 = decode_base58(address)
     node = SimpleNode('tbtc.programmingblockchain.com',
                       testnet=True,
                       logging=False)
     bf = BloomFilter(30, 5, 90210)
     bf.add(h160)
     node.handshake()
     node.send(bf.filterload())
     start_block = bytes.fromhex(last_block_hex)
     getheaders = GetHeadersMessage(start_block=start_block)
     node.send(getheaders)
     headers = node.wait_for(HeadersMessage)
     getdata = GetDataMessage()
     for b in headers.blocks:
         if not b.check_pow():
             raise RuntimeError('proof of work is invalid')
         getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())
     node.send(getdata)
     found = False
     while not found:
         message = node.wait_for(MerkleBlock, Tx)
         if message.command == b'merkleblock':
             if not message.is_valid():
                 raise RuntimeError('invalid merkle proof')
         else:
             for i, tx_out in enumerate(message.tx_outs):
                 if tx_out.script_pubkey.address(testnet=True) == address:
                     self.assertEqual(
                         message.id(),
                         'e3930e1e566ca9b75d53b0eb9acb7607f547e1182d1d22bd4b661cfe18dcddf1'
                     )
                     self.assertEqual(i, 0)
                     found = True
                     break
Ejemplo n.º 16
0
 def test_exercise_6(self):
     last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19'
     secret = little_endian_to_int(
         hash256(b'Jimmy Song Programming Blockchain'))
     private_key = PrivateKey(secret=secret)
     addr = private_key.point.address(testnet=True)
     h160 = decode_base58(addr)
     target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
     self.assertEqual(addr, target_address)
     target_h160 = decode_base58(target_address)
     target_script = p2pkh_script(target_h160)
     fee = 5000
     node = SimpleNode('tbtc.programmingblockchain.com', testnet=True)
     bf = BloomFilter(30, 5, 90210)
     bf.add(h160)
     node.handshake()
     node.send(b'filterload', bf.filterload())
     start_block = bytes.fromhex(last_block_hex)
     getheaders_message = GetHeadersMessage(start_block=start_block)
     node.send(getheaders_message.command, getheaders_message.serialize())
     headers_envelope = node.wait_for_commands({HeadersMessage.command})
     stream = headers_envelope.stream()
     headers = HeadersMessage.parse(stream)
     last_block = None
     get_data_message = GetDataMessage()
     for b in headers.blocks:
         if not b.check_pow():
             raise RuntimeError('proof of work is invalid')
         if last_block is not None and b.prev_block != last_block:
             raise RuntimeError('chain broken')
         get_data_message.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())
         last_block = b.hash()
     node.send(get_data_message.command, get_data_message.serialize())
     prev_tx, prev_index, prev_tx_obj = None, None, None
     while prev_tx is None:
         envelope = node.wait_for_commands({b'merkleblock', b'tx'})
         stream = envelope.stream()
         if envelope.command == b'merkleblock':
             mb = MerkleBlock.parse(stream)
             if not mb.is_valid():
                 raise RuntimeError('invalid merkle proof')
         else:
             prev_tx_obj = Tx.parse(stream, testnet=True)
             for i, tx_out in enumerate(prev_tx_obj.tx_outs):
                 if tx_out.script_pubkey.address(testnet=True) == addr:
                     prev_tx = prev_tx_obj.hash()
                     prev_index = i
                     self.assertEqual(
                         prev_tx_obj.id(),
                         'e3930e1e566ca9b75d53b0eb9acb7607f547e1182d1d22bd4b661cfe18dcddf1'
                     )
                     self.assertEqual(i, 0)
     tx_in = TxIn(prev_tx, prev_index, Script([]), 0xffffff)
     TxFetcher.cache[prev_tx] = prev_tx_obj
     tx_ins = [tx_in]
     total = prev_tx_obj.tx_outs[prev_index].amount
     tx_outs = [TxOut(total - fee, target_script)]
     tx_obj = Tx(1, tx_ins, tx_outs, 0, testnet=True)
     tx_obj.sign_input(0, private_key)
     self.assertEqual(
         tx_obj.serialize().hex(),
         '0100000001f1dddc18fe1c664bbd221d2d18e147f50776cb9aebb0535db7a96c561e0e93e3000000006a473044022046a49962540a89e83da0636455b6c81c11c2844b7f3cd414c02e1a13741f4d15022006eed4eeda994d2bfebb9f1a494bfa3c8bab96e7e4c82623f4a29736dfe309e70121021cdd761c7eb1c90c0af0a5963e94bf0203176b4662778d32bd6d7ab5d8628b32ffffff0001a1629ef5000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000'
     )
Ejemplo n.º 17
0
# Exercise 5
#
# print("Exercise 5")
# run(network.SimpleNodeTest("test_handshake"))

#
# Exercise 6
#
# print("Exercise 6")
# run(network.GetHeadersMessageTest("test_serialize"))

previous = Block.parse(BytesIO(GENESIS_BLOCK))
first_epoch_timestamp = previous.timestamp
expected_bits = LOWEST_BITS
count = 1
node = SimpleNode('mainnet.programmingbitcoin.com', testnet=False)
node.handshake()
for _ in range(19):
    
    getheaders = GetHeadersMessage(start_block=previous.hash())
    node.send(getheaders)
    headers = node.wait_for(HeadersMessage)
    for header in headers.blocks:
        if not header.check_pow():
            raise RuntimeError('bad PoW at block {}'.format(count))
        if header.prev_block != previous.hash():
            raise RuntimeError('discontinuous block at {}'.format(count))
        
        if count % 2016 == 0:
            time_diff = previous.timestamp - first_epoch_timestamp
            expected_bits = calculate_new_bits(previous.bits, time_diff)
Ejemplo n.º 18
0
class Wallet:
    def __init__(self, encrypted_private, public, next_external, next_internal,
                 creation_height, sync_height, utxos, stxos, filename):
        self.encrypted_private = encrypted_private
        self.public = public
        self.next_external = next_external
        self.next_internal = next_internal
        self.creation_height = creation_height
        self.sync_height = sync_height
        self.utxo_lookup = {(utxo.txo.tx_id, utxo.txo.tx_index): utxo
                            for utxo in utxos}
        self.stxo_lookup = {(stxo.spending_tx_id, stxo.spending_tx_index): stxo
                            for stxo in stxos}
        self.filename = filename
        self.testnet = self.public.testnet
        self.tx_store = TxStore.get_store(testnet=self.testnet)
        self.node = SimpleNode(host='192.168.1.200', testnet=self.testnet)
        self.block_store = BlockStore(node=self.node, include=self.sync_height)
        sleep(1)
        if not self.creation_height:
            # this is a new wallet
            self.creation_height = self.sync_height = self.node.latest_block

    @classmethod
    def create(cls, filename=None, testnet=False):
        if filename is None:
            if testnet:
                filename = 'testnet.wallet'
            else:
                filename = 'mainnet.wallet'
        if isfile(filename):
            raise RuntimeError('file exists: {}'.format(filename))
        mnemonic, encrypted_private = EncryptedPrivateKey.generate(testnet)
        if testnet:
            network = b"1'"
        else:
            network = b"0'"
        path = b"m/84'/" + network + b"/0'"
        public = encrypted_private.private_key.traverse(path).pub
        wallet = cls(encrypted_private,
                     public,
                     0,
                     0,
                     None,
                     None, [], [],
                     filename=filename)
        wallet.save()
        return mnemonic, wallet

    @classmethod
    def recover(cls, mnemonic, filename=None, testnet=False):
        if filename is None:
            if testnet:
                filename = 'testnet.wallet'
            else:
                filename = 'mainnet.wallet'
        if isfile(filename):
            raise RuntimeError('file exists: {}'.format(filename))
        encrypted_private = EncryptedPrivateKey.from_mnemonic(
            mnemonic, testnet)
        if testnet:
            network = b"1'"
        else:
            network = b"0'"
        path = b"m/84'/" + network + b"/0'"
        public = encrypted_private.private_key.traverse(path).pub
        wallet = cls(encrypted_private,
                     public,
                     0,
                     0,
                     None,
                     None, [], [],
                     filename=filename)
        # TODO: determine how many addresses have been used
        # TODO: determine what height to start at
        wallet.save()
        return wallet

    @classmethod
    def open(cls, filename='testnet.wallet'):
        if not isfile(filename):
            raise RuntimeError('No such file {}'.format(filename))
        with open(filename, 'rb') as f:
            encrypted_private = EncryptedPrivateKey.parse(f)
            public = HDPublicKey.parse(f)
            next_external = read_varint(f)
            next_internal = read_varint(f)
            creation_height = read_varint(f)
            sync_height = read_varint(f)
            num_utxos = read_varint(f)
            utxos = []
            for _ in range(num_utxos):
                utxos.append(UTXO.parse(f))
            num_stxos = read_varint(f)
            stxos = []
            for _ in range(num_stxos):
                stxos.append(STXO.parse(f))
        return cls(encrypted_private,
                   public,
                   next_external,
                   next_internal,
                   creation_height,
                   sync_height,
                   utxos,
                   stxos,
                   filename=filename)

    def save(self):
        with open(self.filename, 'wb') as f:
            f.write(self.encrypted_private.serialize())
            f.write(self.public.serialize())
            f.write(encode_varint(self.next_external))
            f.write(encode_varint(self.next_internal))
            f.write(encode_varint(self.creation_height))
            f.write(encode_varint(self.sync_height))
            utxos = [u for u in self.utxo_lookup.values()]
            f.write(encode_varint(len(utxos)))
            for utxo in utxos:
                f.write(utxo.serialize())
            stxos = [s for s in self.stxo_lookup.values()]
            f.write(encode_varint(len(stxos)))
            for stxo in stxos:
                f.write(stxo.serialize())

    def address(self):
        pub = self.public.child(0).child(self.next_external)
        self.next_external += 1
        self.save()
        return pub.bech32_address()

    def change_script_pubkey(self):
        pub = self.public.child(1).child(self.next_internal)
        self.next_internal += 1
        self.save()
        return pub.p2wpkh_script_pubkey()

    def script_pubkey_lookup(self):
        script_pubkey_lookup = {}
        external = self.public.child(0)
        for i in range(self.next_external):
            script_pubkey_lookup[external.child(
                i).p2wpkh_script_pubkey().raw_serialize()] = (0, i)
        internal = self.public.child(1)
        for i in range(self.next_internal):
            script_pubkey_lookup[internal.child(
                i).p2wpkh_script_pubkey().raw_serialize()] = (1, i)
        return script_pubkey_lookup

    def rescan(self):
        self.update(self.creation_height)

    def update(self, height=None):
        '''Scan from the block at height to the block that
        the block_store has'''
        self.block_store.update()
        if height is None:
            height = self.sync_height
        block_hashes = self.get_relevant_block_hashes(height)
        self.scan_blocks(block_hashes)

    def get_relevant_block_hashes(self, height):
        # start from height, download the compact filters
        num_requests, r = divmod(self.block_store.store_height() - height,
                                 1000)
        if r > 0:
            num_requests += 1
        block_hashes = []
        script_pubkey_lookup = self.script_pubkey_lookup()
        LOGGER.info('scanning blocks for relevant transactions')
        for i in range(num_requests):
            start_height = height + i * 1000
            if i == num_requests - 1:
                end_height = self.block_store.store_height()
            else:
                end_height = height + (i + 1) * 1000 - 1
            stop_hash = self.block_store.header_by_height(end_height).hash()
            LOGGER.info('scanning from {} to {}'.format(
                start_height, end_height))
            getcfilters = GetCFiltersMessage(
                start_height=start_height,
                stop_hash=stop_hash,
            )
            self.node.send(getcfilters)
            for h in range(start_height, end_height + 1):
                cfilter = self.node.wait_for(CFilterMessage)
                header = self.block_store.header_by_height(h)
                if header.hash() != cfilter.block_hash:
                    raise RuntimeError('bad block')
                computed = hash256(cfilter.filter_bytes)[::-1]
                if computed != header.cfhash:
                    raise RuntimeError('not the right cf hash')
                if script_pubkey_lookup.keys() in cfilter:
                    LOGGER.info('interesting block {}: {}'.format(
                        h, header.id()))
                    header.cfilter = cfilter
                    block_hashes.append(cfilter.block_hash)
        return block_hashes

    def scan_blocks(self, block_hashes):
        script_pubkey_lookup = self.script_pubkey_lookup()
        # download the blocks we know are interesting
        for h, b in self.node.get_blocks(block_hashes):
            if h != b.hash():
                raise RuntimeError('bad block: {} vs {}'.format(
                    h.hex(), b.id()))
            header = self.block_store.header_by_hash(h)
            for raw_script_pubkey in b.get_outpoints():
                if raw_script_pubkey not in header.cfilter:
                    raise RuntimeError('script_pubkeys are not in the filter')
            for tx_obj in b.txs:
                add_tx = False
                tx_hash = tx_obj.hash()
                if not tx_obj.is_coinbase():
                    for i, tx_in in enumerate(tx_obj.tx_ins):
                        # if this transaction has a utxo as an input (spending)
                        key = (tx_in.prev_tx, tx_in.prev_index)
                        if self.utxo_lookup.get(key):
                            add_tx = True
                            utxo = self.utxo_lookup.pop(key)
                            self.stxo_lookup[key] = STXO(utxo.txo, tx_hash, i)
                for i, tx_out in enumerate(tx_obj.tx_outs):
                    # if this transaction has an output that's ours (receiving)
                    raw_script_pubkey = tx_out.script_pubkey.raw_serialize()
                    if raw_script_pubkey in script_pubkey_lookup.keys():
                        add_tx = True
                        txo = TXO(tx_hash, i, tx_out.amount,
                                  tx_out.script_pubkey,
                                  script_pubkey_lookup[raw_script_pubkey])
                        self.utxo_lookup[key] = UTXO(txo)
                if add_tx:
                    LOGGER.info('interesting tx {}'.format(tx_obj.id()))
                    self.tx_store.add(tx_obj)
        self.sync_height = self.block_store.store_height()
        self.save()
        self.tx_store.save()

    def balance(self):
        balance = 0
        for utxo in self.utxo_lookup.values():
            balance += utxo.txo.amount
        return balance

    def spent(self):
        spent = 0
        for stxo in self.stxo_lookup.values():
            spent += stxo.txo.amount
        return spent

    def total_received(self):
        return self.balance() + self.spent()

    def simple_send(self, address, amount, satoshi_per_byte=1):
        if amount > self.balance():
            raise RuntimeError(
                'Balance is less than how much you want to send')
        input_total = 0
        tx_ins = []
        paths = []
        # gather inputs using our utxos
        for utxo in self.utxo_lookup.values():
            input_total += utxo.txo.amount
            tx_ins.append(TxIn(utxo.txo.tx_id, utxo.txo.tx_index))
            paths.append(utxo.txo.path)
            if input_total > amount:
                break

        # target output
        tx_outs = [TxOut(amount, address_to_script_pubkey(address))]
        tx_outs_size = len(tx_outs[0].serialize())

        # calculate how much fees should be at this point
        # our utxos are always bech32, so each input is 32+4+1+4 = 41 bytes
        # our change outputs are bech32, so each output is 8+23 = 31 bytes
        # the witness will be 33 + 72 bytes per input but count 1/4
        tx_size = 4 + 1 + len(tx_ins) * 41 + 1 + tx_outs_size + 31 + len(
            tx_ins) * (2 + 33 + 72)
        fee = tx_size * satoshi_per_byte
        if input_total < fee + amount:
            raise RuntimeError('Balance does not cover fees')

        # change output
        change_amount = input_total - amount - fee
        # don't bother if we're creating dust
        if change_amount > 200:
            tx_outs.append(TxOut(change_amount, self.change_script_pubkey()))

        # create transaction and sign
        tx_obj = Tx(1, tx_ins, tx_outs, 0, testnet=self.testnet, segwit=True)
        # signing the transaction
        self.encrypted_private.sign(tx_obj, paths)
        LOGGER.info('sending the transaction')
        return self.node.send_tx(tx_obj)
Ejemplo n.º 19
0
#
# Exercise 6
#
print("Exercise 6")

block_hash_hex = "00000000000001c56dc26dd2ad91860a7fe3fe23bbf00f09ff378f1f69bea512"

# utxo_address = "mqJ8CrnEEUbeMFQ628e99P3RAATbarrQwK"
# utxo_address_h160 = decode_base58(utxo_address)

# bf = BloomFilter(size=30, function_count=5, tweak=45324)
# bf.add(utxo_address_h160)

node = SimpleNode('testnet.programmingbitcoin.com',
                  testnet=True,
                  logging=False)
# node.handshake()
# node.send(bf.filterload())

# getdata = GetDataMessage()
# getdata.add_data(FILTERED_BLOCK_DATA_TYPE, bytes.fromhex(block_hash_hex))

# node.send(getdata)

# found = False
# while not found:
#     message = node.wait_for(MerkleBlock, Tx)
#     if message.command == b'merkleblock':
#         if not message.is_valid():
#             raise RuntimeError('invalid merkle proof')
Ejemplo n.º 20
0
from tx import Tx, TxIn, TxOut, TxFetcher

last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19'

secret = little_endian_to_int(hash256(b''))
private_key = PrivateKey(secret=secret)
addr = private_key.point.address(testnet=True)
h160 = decode_base58(addr)

target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
target_h160 = decode_base58(target_address)
target_script = p2pkh_script(target_h160)
fee = 5000  # fee in satoshis

# connect to tbtc.programmingblockchain.com in testnet mode
node = SimpleNode('tbtc.programmingblockchain.com', testnet=True, logging=True)

# create a bloom filter of size 30 and 5 functions. Add a tweak that you like
bf = BloomFilter(30, 5, 90210)
# add the h160 to the bloom filter
bf.add(h160)

# complete the handshake
node.handshake()
# load the bloom filter with the filterload command
node.send(bf.filterload())

# set start block to last_block from above
start_block = bytes.fromhex(last_block_hex)
# send a getheaders message with the starting block
getheaders = GetHeadersMessage(start_block=start_block)