def test_headers_message_one_block_not_in_flight(self): header1 = CBlockHeader(nNonce=1) header2 = CBlockHeader(nNonce=2) message = messages.msg_headers() message.headers = [header1, header2] self.networking.blocks_in_flight = {header1.GetHash(): 'in_flight'} self.networking.headers_message(self.private_connection, message) self.assertEqual(self.private_connection.send.call_args[0][0], 'getdata') self.assertEqual(len(self.private_connection.send.call_args[0][1].inv), 1)
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 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 _commit_block(self, height, block_id, hash_of_previous, bits, timestamp, target): cursor = self.db.cursor() cursor.execute('''SELECT totalWork FROM blocks WHERE height=?''', (height-1,)) total_work = cursor.fetchone()[0] + CBlockHeader.calc_difficulty(bits) cursor = self.db.cursor() cursor.execute('''INSERT INTO blocks(totalWork, height, blockID, hashOfPrevious, timestamp, target) VALUES (?,?,?,?,?,?)''', (total_work, height, block_id, hash_of_previous, timestamp, target)) self.db.commit() self._cull()
def header_from_insight_block(d): version = int(d['version']) prev_block = lx(d['previousblockhash']) merkle_root = lx(d['merkleroot']) time = int(d['time']) bits = int(d['bits'], 16) nonce = int(d['nonce']) return b2x( CBlockHeader(version, prev_block, merkle_root, time, bits, nonce).serialize())
def get_header(self): """Return the block header Returned header is a new object. """ return CBlockHeader(nVersion=self.nVersion, hashPrevBlock=self.hashPrevBlock, hashMerkleRoot=self.hashMerkleRoot, nTime=self.nTime, nBits=self.nBits, nNonce=self.nNonce)
def deserialize(self, raw): """Deserialize hex-encoded block/block header.""" only_header = False if len(raw) == 160: only_header = True block = None block_header = None try: if only_header: block_header = CBlockHeader.deserialize(x(raw)) else: # We don't use block.get_header() in case the header is # correct but the rest of the block isn't. block_header = CBlockHeader.deserialize(x(raw[0:160])) block = CBlock.deserialize(x(raw)) except Exception: pass return (block, block_header)
def get_block_header(self, block_hash: bytes, verbose: bool = False) -> CBlockHeader: payload = { "method": "getblockheader", "params": [block_hash.hex(), verbose], "jsonrpc": "2.0", "id": 0, } json_response = self.make_request(payload) result = json_response["result"] header_bytes = bytes.fromhex(result) return CBlockHeader.deserialize(header_bytes)
def _commit_block(self, height, block_id, hash_of_previous, bits, timestamp, target): cursor = self.db.cursor() cursor.execute('''SELECT totalWork FROM blocks WHERE height=?''', (height - 1, )) total_work = cursor.fetchone()[0] + CBlockHeader.calc_difficulty(bits) cursor = self.db.cursor() cursor.execute( '''INSERT INTO blocks(totalWork, height, blockID, hashOfPrevious, timestamp, target) VALUES (?,?,?,?,?,?)''', (total_work, height, block_id, hash_of_previous, timestamp, target)) self.db.commit() self._cull()
def getblockheader(self, block_hash): """Get block header <block_hash> 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, False) except JSONRPCError as ex: raise IndexError('%s.getblockheader(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) return CBlockHeader.deserialize(unhexlify(r))
def getblockheader(self, block_hash): """Get block header <block_hash> 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, False) except JSONRPCError as ex: raise IndexError('%s.getblockheader(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) return CBlockHeader.deserialize(unhexlify(r))
def getblockheader(self, block_hash): """Get block header <block_hash> Raises IndexError if block_hash is not valid. There's no getblockheader RPC command, so it actually calls getblock but only deserializes the first 80 bytes. """ try: block_hash = b2lx(block_hash) except TypeError: raise TypeError('%s.getblock(): block_hash must be bytes; got %r instance' % (self.__class__.__name__, block_hash.__class__)) try: r = self._call('getblock', block_hash, False) except JSONRPCException as ex: raise IndexError('%s.getblockheader(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) return CBlockHeader.deserialize(unhexlify(r[0:80*2]))
def getblockheader(self, block_hash): """Get block header <block_hash> Raises IndexError if block_hash is not valid. There's no getblockheader RPC command, so it actually calls getblock but only deserializes the first 80 bytes. """ try: block_hash = b2lx(block_hash) except TypeError: raise TypeError( '%s.getblock(): block_hash must be bytes; got %r instance' % (self.__class__.__name__, block_hash.__class__)) try: r = self._call('getblock', block_hash, False) except JSONRPCException as ex: raise IndexError('%s.getblockheader(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) return CBlockHeader.deserialize(unhexlify(r[0:80 * 2]))
def test_headers_message_two_unknown_blocks(self): header1 = CBlockHeader(nNonce=1) header2 = CBlockHeader(nNonce=2) message = messages.msg_headers() message.headers = [header1, header2] self.networking.blocks_in_flight = {} self.networking.request_blocks = MagicMock() self.networking.headers_message(self.private_connection, message) self.assertEqual(self.chain.process_header.call_count, 2) self.assertEqual(self.chain.process_header.call_args[0][1], BlockOrigin.private) self.assertTrue(self.networking.request_blocks.called) self.assertEqual(len(self.networking.request_blocks.call_args[0][1]), 2) self.assertIn(header1.GetHash(), self.networking.request_blocks.call_args[0][1]) self.assertIn(header2.GetHash(), self.networking.request_blocks.call_args[0][1])
def setUp(self): self.first_block_chain_a = Block(None, BlockOrigin.private) self.first_block_chain_a.height = 1 self.first_block_chain_a.prevBlock = test_util.genesis_block self.first_block_chain_a.cached_hash = '1a' self.first_block_chain_a.cblock_header = CBlockHeader(nNonce=11) self.first_block_chain_a.cblock = CBlock(nNonce=11) self.second_block_chain_a = Block(None, BlockOrigin.private) self.second_block_chain_a.height = 2 self.second_block_chain_a.prevBlock = self.first_block_chain_a self.second_block_chain_a.cached_hash = '2a' self.second_block_chain_a.cblock_header = CBlockHeader(nNonce=21) self.second_block_chain_a.cblock = CBlock(nNonce=21) self.third_a_block_chain_a = Block(None, BlockOrigin.private) self.third_a_block_chain_a.height = 3 self.third_a_block_chain_a.prevBlock = self.second_block_chain_a self.third_a_block_chain_a.cached_hash = '3a_1' self.third_a_block_chain_a.cblock_header = CBlockHeader(nNonce=311) self.third_a_block_chain_a.cblock = CBlock(nNonce=311) self.third_b_block_chain_a = Block(None, BlockOrigin.private) self.third_b_block_chain_a.height = 3 self.third_b_block_chain_a.prevBlock = self.second_block_chain_a self.third_b_block_chain_a.cached_hash = '3a_2' self.third_b_block_chain_a.cblock_header = CBlockHeader(nNonce=312) self.third_b_block_chain_a.cblock = CBlock(nNonce=312) self.fourth_block_chain_a = Block(None, BlockOrigin.private) self.fourth_block_chain_a.height = 4 self.fourth_block_chain_a.prevBlock = self.third_a_block_chain_a self.fourth_block_chain_a.cached_hash = '4a' self.fourth_block_chain_a.cblock_header = CBlockHeader(nNonce=41) self.fourth_block_chain_a.cblock = CBlock(nNonce=41) self.first_block_chain_b = Block(None, BlockOrigin.public) self.first_block_chain_b.height = 1 self.first_block_chain_b.prevBlock = test_util.genesis_block self.first_block_chain_b.cached_hash = '1b' self.first_block_chain_b.cblock_header = CBlockHeader(nNonce=12) self.first_block_chain_b.cblock = CBlock(nNonce=12) self.second_block_chain_b = Block(None, BlockOrigin.public) self.second_block_chain_b.height = 2 self.second_block_chain_b.prevBlock = self.first_block_chain_b self.second_block_chain_b.cached_hash = '2b' self.second_block_chain_b.cblock_header = CBlockHeader(nNonce=22) self.second_block_chain_b.cblock = CBlock(nNonce=22) self.third_a_block_chain_b = Block(None, BlockOrigin.public) self.third_a_block_chain_b.height = 3 self.third_a_block_chain_b.prevBlock = self.second_block_chain_b self.third_a_block_chain_b.cached_hash = '3b_1' self.third_a_block_chain_b.cblock_header = CBlockHeader(nNonce=321) self.third_a_block_chain_b.cblock = CBlock(nNonce=321) self.third_b_block_chain_b = Block(None, BlockOrigin.public) self.third_b_block_chain_b.height = 3 self.third_b_block_chain_b.prevBlock = self.second_block_chain_b self.third_b_block_chain_b.cached_hash = '3b_2' self.third_b_block_chain_b.cblock_header = CBlockHeader(nNonce=322) self.third_b_block_chain_b.cblock = CBlock(nNonce=322) self.fourth_block_chain_b = Block(None, BlockOrigin.public) self.fourth_block_chain_b.height = 4 self.fourth_block_chain_b.prevBlock = self.third_a_block_chain_b self.fourth_block_chain_b.cached_hash = '4b' self.fourth_block_chain_b.cblock_header = CBlockHeader(nNonce=42) self.fourth_block_chain_b.cblock = CBlock(nNonce=42)
def block_header(block_header_bytes): yield CBlockHeader.deserialize(block_header_bytes)
def connect(self): self.send_lock.acquire() self.recv_transaction_cache = FlaggedArraySet(1000) self.send_transaction_cache = FlaggedArraySet(1000) self.relay_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.relay_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) try: try: self.relay_sock.connect((self.server, 8336)) self.relay_sock.sendall(pack('>3I', self.MAGIC_BYTES, self.VERSION_TYPE, len(self.VERSION_STRING))) self.relay_sock.sendall(self.VERSION_STRING) finally: self.send_lock.release() while True: msg_header = unpack('>3I', self.relay_sock.recv(3 * 4, socket.MSG_WAITALL)) if msg_header[0] != self.MAGIC_BYTES: raise ProtocolError("Invalid magic bytes: " + str(msg_header[0]) + " != " + str(self.MAGIC_BYTES)) if msg_header[2] > 1000000: raise ProtocolError("Got message too large: " + str(msg_header[2])) if msg_header[1] == self.VERSION_TYPE: version = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) if version != self.VERSION_STRING: raise ProtocolError("Got back unknown version type " + str(version)) print("Connected to relay node with protocol version " + str(version)) elif msg_header[1] == self.BLOCK_TYPE: if msg_header[2] > 10000: raise ProtocolError("Got a BLOCK message with far too many transactions: " + str(msg_header[2])) wire_bytes = 3 * 4 header_data = self.relay_sock.recv(80, socket.MSG_WAITALL) wire_bytes += 80 self.data_recipient.provide_block_header(header_data) if deserialize_utils: header = CBlockHeader.deserialize(header_data) print("Got block header: " + str(b2lx(header.GetHash()))) if msg_header[2] < 0xfd: block_data = header_data + pack('B', msg_header[2]) elif msg_header[2] < 0xffff: block_data = header_data + b'\xfd' + pack('<H', msg_header[2]) elif msg_header[2] < 0xffffffff: block_data = header_data + b'\xfe' + pack('<I', msg_header[2]) else: raise ProtocolError("WTF?????") for i in range(0, msg_header[2]): index = unpack('>H', self.relay_sock.recv(2, socket.MSG_WAITALL))[0] wire_bytes += 2 if index == 0xffff: data_length = unpack('>HB', self.relay_sock.recv(3, socket.MSG_WAITALL)) wire_bytes += 3 data_length = data_length[0] << 8 | data_length[1] if data_length > 1000000: raise ProtocolError("Got in-block transaction of size > MAX_BLOCK_SIZE: " + str(dat_length)) transaction_data = self.relay_sock.recv(data_length, socket.MSG_WAITALL) wire_bytes += data_length if deserialize_utils: transaction = CTransaction.deserialize(transaction_data) print("Got in-block full transaction: " + str(b2lx(transaction.GetHash())) + " of length " + str(data_length)) else: print("Got in-block full transaction of length " + str(data_length)) block_data += transaction_data else: transaction_data = self.recv_transaction_cache.get_by_index(index) if transaction_data is None: raise ProtocolError("Got index for a transaction we didn't have") self.recv_transaction_cache.remove(transaction_data) block_data += transaction_data self.data_recipient.provide_block(block_data) if deserialize_utils: print("Got full block " + str(b2lx(header.GetHash())) + " with " + str(msg_header[2]) + " transactions in " + str(wire_bytes) + " wire bytes") block = CBlock.deserialize(block_data) print("Deserialized full block " + str(b2lx(block.GetHash()))) else: print("Got full block with " + str(msg_header[2]) + " transactions in " + str(wire_bytes) + " wire bytes") if unpack('>3I', self.relay_sock.recv(3 * 4, socket.MSG_WAITALL)) != (self.MAGIC_BYTES, self.END_BLOCK_TYPE, 0): raise ProtocolError("Invalid END_BLOCK message after block") elif msg_header[1] == self.TRANSACTION_TYPE: if msg_header[2] > self.MAX_RELAY_TRANSACTION_BYTES and (self.recv_transaction_cache.get_flag_count() >= self.MAX_EXTRA_OVERSIZE_TRANSACTIONS or msg_header[2] > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES): raise ProtocolError("Got a freely relayed transaction too large (" + str(msg_header[2]) + ") bytes") transaction_data = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) self.recv_transaction_cache.add(transaction_data, msg_header[2] > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES) self.data_recipient.provide_transaction(transaction_data) if deserialize_utils: transaction = CTransaction.deserialize(transaction_data) print("Got transaction: " + str(b2lx(transaction.GetHash()))) else: print("Got transaction of length " + str(msg_header[2])) elif msg_header[1] == self.MAX_VERSION_TYPE: version = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) print("Relay network now uses version " + str(version) + " (PLEASE UPGRADE)") else: raise ProtocolError("Unknown message type: " + str(msg_header[1])) except (OSError, socket.error) as err: print("Lost connect to relay node:", err) self.reconnect() except ProtocolError as err: print("Error processing data from relay node:", err) self.reconnect() except Exception as err: print("Unknown error processing data from relay node:", err) self.reconnect()
def difficulty(self): """Returns the block's difficulty target as a float""" if self._difficulty is None: self._difficulty = CBlockHeader.calc_difficulty(self.bits) return self._difficulty
def gen_block_header(block_height): return CBlockHeader( nTime=block_height * 10, # So that block times are increasing. )