def from_block_hash_number_pair(cls, block_hash: Sha256Hash, number: int) -> "NewBlockHashesEthProtocolMessage": block_hash_bytes = block_hash.binary msg_size = len(block_hash_bytes) block_hash_prefix = rlp_utils.get_length_prefix_str(len(block_hash_bytes)) msg_size += len(block_hash_prefix) number_bytes = rlp_utils.encode_int(number) msg_size += len(number_bytes) block_hash_and_number_prefix = rlp_utils.get_length_prefix_list(msg_size) msg_size += len(block_hash_and_number_prefix) block_hashes_list_prefix = rlp_utils.get_length_prefix_list(msg_size) msg_size += len(block_hashes_list_prefix) msg_bytes = bytearray(msg_size) offset = 0 msg_bytes[offset:len(block_hashes_list_prefix)] = block_hashes_list_prefix offset += len(block_hashes_list_prefix) msg_bytes[offset:offset + len(block_hash_and_number_prefix)] = block_hash_and_number_prefix offset += len(block_hash_and_number_prefix) msg_bytes[offset:offset + len(block_hash_prefix)] = block_hash_prefix offset += len(block_hash_prefix) msg_bytes[offset:offset + len(block_hash_bytes)] = block_hash_bytes offset += len(block_hash_bytes) msg_bytes[offset:offset + len(number_bytes)] = number_bytes return cls(msg_bytes)
def test_get_length_prefix_list(self): self.assertEqual(b"\xc1", rlp_utils.get_length_prefix_list(1)) self.assertEqual(b"\xc2", rlp_utils.get_length_prefix_list(2)) self.assertEqual(b"\xf8\x38", rlp_utils.get_length_prefix_list(56)) self.assertEqual(b"\xf8\x80", rlp_utils.get_length_prefix_list(128)) self.assertEqual(b"\xf9\x04\x00", rlp_utils.get_length_prefix_list(1024)) self.assertRaises(ValueError, rlp_utils.get_length_prefix_str, None) self.assertRaises(ValueError, rlp_utils.get_length_prefix_str, 256 ** 8)
def from_header_bytes(cls, header_bytes: memoryview) -> "BlockHeadersEthProtocolMessage": headers_list_prefix = rlp_utils.get_length_prefix_list(len(header_bytes)) msg_bytes = bytearray(len(headers_list_prefix) + len(header_bytes)) msg_bytes[:len(headers_list_prefix)] = headers_list_prefix msg_bytes[len(headers_list_prefix):] = header_bytes return cls(msg_bytes)
def from_new_block_parts( cls, new_block_parts: NewBlockParts, total_difficulty: int) -> "NewBlockEthProtocolMessage": block_header = memoryview(new_block_parts.block_header_bytes) block_body = memoryview(new_block_parts.block_body_bytes) _, block_content_len, block_content_start = rlp_utils.consume_length_prefix( block_body, 0) block_body_conent = block_body[block_content_start:] content_size = len(block_header) + len(block_body_conent) block_item_prefix = rlp_utils.get_length_prefix_list(content_size) content_size += len(block_item_prefix) total_difficulty_item = rlp_utils.encode_int(total_difficulty) content_size += len(total_difficulty_item) new_block_item_prefix = rlp_utils.get_length_prefix_list(content_size) content_size += len(new_block_item_prefix) written_bytes = 0 new_block_bytes = bytearray(content_size) new_block_bytes[written_bytes:written_bytes + len(new_block_item_prefix)] = new_block_item_prefix written_bytes += len(new_block_item_prefix) new_block_bytes[written_bytes:written_bytes + len(block_item_prefix)] = block_item_prefix written_bytes += len(block_item_prefix) new_block_bytes[written_bytes:written_bytes + len(block_header)] = block_header written_bytes += len(block_header) new_block_bytes[written_bytes:written_bytes + len(block_body_conent)] = block_body_conent written_bytes += len(block_body_conent) new_block_bytes[written_bytes:written_bytes + len(total_difficulty_item)] = total_difficulty_item written_bytes += len(total_difficulty_item) return cls(new_block_bytes)
def from_new_block_msg( cls, new_block_msg: NewBlockEthProtocolMessage ) -> "InternalEthBlockInfo": """ Creates NewBlockInternalEthMessage from raw bytes of NewBlockEthProtocolMessage :param new_block_msg: new block message :return: NewBlockInternalEthMessage message """ new_block_msg_bytes = memoryview(new_block_msg.rawbytes()) _, block_msg_itm_len, block_msg_itm_start = rlp_utils.consume_length_prefix( new_block_msg_bytes, 0) block_msg_bytes = new_block_msg_bytes[ block_msg_itm_start:block_msg_itm_start + block_msg_itm_len] msg_size = 0 # block item already include header, transactions and uncles _, block_itm_len, block_itm_start = rlp_utils.consume_length_prefix( block_msg_bytes, 0) block_itm_bytes = block_msg_bytes[ block_msg_itm_start:block_msg_itm_start + block_itm_len] msg_size += len(block_itm_bytes) difficulty_bytes = block_msg_bytes[block_msg_itm_start + block_itm_len:] msg_size += len(difficulty_bytes) block_number_bytes = rlp_utils.encode_int(new_block_msg.number()) msg_size += len(block_number_bytes) msg_prefix = rlp_utils.get_length_prefix_list( len(block_itm_bytes) + len(difficulty_bytes) + len(block_number_bytes)) msg_size += len(msg_prefix) msg_bytes = bytearray(msg_size) written_bytes = 0 msg_bytes[written_bytes:written_bytes + len(msg_prefix)] = msg_prefix written_bytes += len(msg_prefix) msg_bytes[written_bytes:written_bytes + len(block_itm_bytes)] = block_itm_bytes written_bytes += len(block_itm_bytes) msg_bytes[written_bytes:written_bytes + len(difficulty_bytes)] = difficulty_bytes written_bytes += len(difficulty_bytes) msg_bytes[written_bytes:written_bytes + len(block_number_bytes)] = block_number_bytes written_bytes += len(block_number_bytes) assert written_bytes == msg_size return cls(msg_bytes)
def from_new_block_parts( cls, new_block_details: NewBlockParts, total_difficulty: Optional[int] = 0) -> "InternalEthBlockInfo": """ Creates NewBlockInternalEthMessage from block header and block body bytes :param new_block_details: new block details :param total_difficulty: total difficulty of the block if known :return: instance of NewBlockInternalEthMessage """ block_header_bytes = new_block_details.block_header_bytes block_body_bytes = new_block_details.block_body_bytes block_number = new_block_details.block_number # block body content includes transactions and uncles _, block_content_len, block_content_start = rlp_utils.consume_length_prefix( block_body_bytes, 0) block_body_conent = block_body_bytes[block_content_start:] msg_size = len(block_header_bytes) + len(block_body_conent) total_difficulty_item = rlp_utils.encode_int( 0 if total_difficulty is None else total_difficulty) msg_size += len(total_difficulty_item) block_number_bytes = rlp_utils.encode_int(block_number) msg_size += len(block_number_bytes) new_block_item_prefix = rlp_utils.get_length_prefix_list(msg_size) msg_size += len(new_block_item_prefix) written_bytes = 0 new_block_bytes = bytearray(msg_size) new_block_bytes[written_bytes:written_bytes + len(new_block_item_prefix)] = new_block_item_prefix written_bytes += len(new_block_item_prefix) new_block_bytes[written_bytes:written_bytes + len(block_header_bytes)] = block_header_bytes written_bytes += len(block_header_bytes) new_block_bytes[written_bytes:written_bytes + len(block_body_conent)] = block_body_conent written_bytes += len(block_body_conent) new_block_bytes[written_bytes:written_bytes + len(total_difficulty_item)] = total_difficulty_item written_bytes += len(total_difficulty_item) new_block_bytes[written_bytes:written_bytes + len(block_number_bytes)] = block_number_bytes written_bytes += len(block_number_bytes) assert msg_size == written_bytes return cls(new_block_bytes)
def from_body_bytes( cls, body_bytes: memoryview) -> "BlockBodiesEthProtocolMessage": bodies_list_prefix = rlp_utils.get_length_prefix_list(len(body_bytes)) msg_bytes = bytearray(len(bodies_list_prefix) + len(body_bytes)) msg_bytes[:len(bodies_list_prefix)] = bodies_list_prefix msg_bytes[len(bodies_list_prefix):] = body_bytes return cls(msg_bytes)
def parse_transaction_bytes( tx_bytes: memoryview) -> TransactionsEthProtocolMessage: size = len(tx_bytes) txs_prefix = rlp_utils.get_length_prefix_list(size) size += len(txs_prefix) buf = bytearray(size) buf[0:len(txs_prefix)] = txs_prefix buf[len(txs_prefix):] = tx_bytes return TransactionsEthProtocolMessage(buf)
def to_new_block_parts(self) -> NewBlockParts: _, msg_itm_len, msg_itm_start = rlp_utils.consume_length_prefix( self._memory_view, 0) msg_itm_bytes = self._memory_view[msg_itm_start:] offset = 0 _, header_len, header_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) header_bytes = msg_itm_bytes[offset:header_start + header_len] offset = header_start + header_len _, txs_len, txs_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) txs_bytes = msg_itm_bytes[offset:txs_start + txs_len] offset = txs_start + txs_len _, uncles_len, uncles_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) uncles_bytes = msg_itm_bytes[offset:uncles_start + uncles_len] offset = uncles_start + uncles_len _, total_difficulty_len, total_difficulty_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) offset = total_difficulty_start + total_difficulty_len block_number, _ = rlp_utils.decode_int(msg_itm_bytes, offset) block_body_prefix = rlp_utils.get_length_prefix_list( len(txs_bytes) + len(uncles_bytes)) block_body_bytes = bytearray( len(block_body_prefix) + len(txs_bytes) + len(uncles_bytes)) block_body_bytes[:len(block_body_prefix)] = block_body_prefix written_bytes = len(block_body_prefix) block_body_bytes[written_bytes:written_bytes + len(txs_bytes)] = txs_bytes written_bytes += len(txs_bytes) block_body_bytes[written_bytes:written_bytes + len(uncles_bytes)] = uncles_bytes written_bytes += len(uncles_bytes) return NewBlockParts(header_bytes, block_body_bytes, block_number)
def block_to_bx_block( self, block_msg: InternalEthBlockInfo, tx_service: TransactionService, enable_block_compression: bool, min_tx_age_seconds: float) -> Tuple[memoryview, BlockInfo]: """ Convert Ethereum new block message to internal broadcast message with transactions replaced with short ids The code is optimized and does not make copies of bytes :param block_msg: Ethereum new block message :param tx_service: Transactions service :param enable_block_compression :param min_tx_age_seconds :return: Internal broadcast message bytes (bytearray), tuple (txs count, previous block hash) """ compress_start_datetime = datetime.datetime.utcnow() compress_start_timestamp = time.time() txs_bytes, block_hdr_full_bytes, remaining_bytes, prev_block_bytes = parse_block_message( block_msg) used_short_ids = [] # creating transactions content content_size = 0 buf = deque() ignored_sids = [] tx_start_index = 0 tx_count = 0 original_size = len(block_msg.rawbytes()) max_timestamp_for_compression = time.time() - min_tx_age_seconds while True: if tx_start_index >= len(txs_bytes): break _, tx_item_length, tx_item_start = rlp_utils.consume_length_prefix( txs_bytes, tx_start_index) tx_bytes = txs_bytes[tx_start_index:tx_item_start + tx_item_length] tx_hash_bytes = eth_common_utils.keccak_hash(tx_bytes) tx_hash = Sha256Hash(tx_hash_bytes) tx_key = tx_service.get_transaction_key(tx_hash) short_id = tx_service.get_short_id_by_key(tx_key) short_id_assign_time = 0 if short_id != constants.NULL_TX_SID: short_id_assign_time = tx_service.get_short_id_assign_time( short_id) if short_id <= constants.NULL_TX_SID or \ not enable_block_compression or short_id_assign_time > max_timestamp_for_compression: if short_id > constants.NULL_TX_SID: ignored_sids.append(short_id) is_full_tx_bytes = rlp_utils.encode_int(1) tx_content_bytes = tx_bytes else: is_full_tx_bytes = rlp_utils.encode_int(0) used_short_ids.append(short_id) tx_content_bytes = bytes() tx_content_prefix = rlp_utils.get_length_prefix_str( len(tx_content_bytes)) short_tx_content_size = len(is_full_tx_bytes) + len( tx_content_prefix) + len(tx_content_bytes) short_tx_content_prefix_bytes = rlp_utils.get_length_prefix_list( short_tx_content_size) buf.append(short_tx_content_prefix_bytes) buf.append(is_full_tx_bytes) buf.append(tx_content_prefix) buf.append(tx_content_bytes) content_size += len( short_tx_content_prefix_bytes) + short_tx_content_size tx_start_index = tx_item_start + tx_item_length tx_count += 1 list_of_txs_prefix_bytes = rlp_utils.get_length_prefix_list( content_size) buf.appendleft(list_of_txs_prefix_bytes) content_size += len(list_of_txs_prefix_bytes) buf.appendleft(block_hdr_full_bytes) content_size += len(block_hdr_full_bytes) buf.append(remaining_bytes) content_size += len(remaining_bytes) compact_block_msg_prefix = rlp_utils.get_length_prefix_list( content_size) buf.appendleft(compact_block_msg_prefix) content_size += len(compact_block_msg_prefix) block = finalize_block_bytes(buf, content_size, used_short_ids) bx_block_hash = convert.bytes_to_hex(crypto.double_sha256(block)) block_info = BlockInfo(block_msg.block_hash(), used_short_ids, compress_start_datetime, datetime.datetime.utcnow(), (time.time() - compress_start_timestamp) * 1000, tx_count, bx_block_hash, convert.bytes_to_hex(prev_block_bytes), original_size, content_size, 100 - float(content_size) / original_size * 100, ignored_sids) return memoryview(block), block_info
def bx_block_to_block(self, bx_block_msg, tx_service) -> BlockDecompressionResult: """ Converts internal broadcast message to Ethereum new block message The code is optimized and does not make copies of bytes :param bx_block_msg: internal broadcast message bytes :param tx_service: Transactions service :return: tuple (new block message, block hash, unknown transaction short id, unknown transaction hashes) """ if not isinstance(bx_block_msg, (bytearray, memoryview)): raise TypeError( "Type bytearray is expected for arg block_bytes but was {0}". format(type(bx_block_msg))) decompress_start_datetime = datetime.datetime.utcnow() decompress_start_timestamp = time.time() block_msg_bytes = bx_block_msg if isinstance( bx_block_msg, memoryview) else memoryview(bx_block_msg) block_offsets = compact_block_short_ids_serializer.get_bx_block_offsets( bx_block_msg) short_ids, short_ids_bytes_len = compact_block_short_ids_serializer.deserialize_short_ids_from_buffer( bx_block_msg, block_offsets.short_id_offset) block_bytes = block_msg_bytes[ block_offsets.block_begin_offset:block_offsets.short_id_offset] _, block_itm_len, block_itm_start = rlp_utils.consume_length_prefix( block_bytes, 0) block_itm_bytes = block_bytes[block_itm_start:] _, block_hdr_len, block_hdr_start = rlp_utils.consume_length_prefix( block_itm_bytes, 0) full_hdr_bytes = block_itm_bytes[0:block_hdr_start + block_hdr_len] block_hash_bytes = eth_common_utils.keccak_hash(full_hdr_bytes) block_hash = Sha256Hash(block_hash_bytes) _, block_txs_len, block_txs_start = rlp_utils.consume_length_prefix( block_itm_bytes, block_hdr_start + block_hdr_len) txs_bytes = block_itm_bytes[block_txs_start:block_txs_start + block_txs_len] remaining_bytes = block_itm_bytes[block_txs_start + block_txs_len:] # parse statistics variables short_tx_index = 0 unknown_tx_sids = [] unknown_tx_hashes = [] # creating transactions content content_size = 0 buf = deque() tx_count = 0 tx_start_index = 0 while True: if tx_start_index >= len(txs_bytes): break _, tx_itm_len, tx_itm_start = rlp_utils.consume_length_prefix( txs_bytes, tx_start_index) tx_bytes = txs_bytes[tx_itm_start:tx_itm_start + tx_itm_len] is_full_tx_start = 0 is_full_tx, is_full_tx_len, = rlp_utils.decode_int( tx_bytes, is_full_tx_start) _, tx_content_len, tx_content_start = rlp_utils.consume_length_prefix( tx_bytes, is_full_tx_start + is_full_tx_len) tx_content_bytes = tx_bytes[tx_content_start:tx_content_start + tx_content_len] if is_full_tx: tx_bytes = tx_content_bytes else: short_id = short_ids[short_tx_index] tx_hash, tx_bytes, _ = tx_service.get_transaction(short_id) if tx_hash is None: unknown_tx_sids.append(short_id) elif tx_bytes is None: unknown_tx_hashes.append(tx_hash) short_tx_index += 1 if tx_bytes is not None and not unknown_tx_sids and not unknown_tx_hashes: buf.append(tx_bytes) content_size += len(tx_bytes) tx_count += 1 tx_start_index = tx_itm_start + tx_itm_len if not unknown_tx_sids and not unknown_tx_hashes: txs_prefix = rlp_utils.get_length_prefix_list(content_size) buf.appendleft(txs_prefix) content_size += len(txs_prefix) buf.appendleft(full_hdr_bytes) content_size += len(full_hdr_bytes) buf.append(remaining_bytes) content_size += len(remaining_bytes) msg_len_prefix = rlp_utils.get_length_prefix_list(content_size) buf.appendleft(msg_len_prefix) block_msg_bytes = bytearray(content_size) off = 0 for blob in buf: next_off = off + len(blob) block_msg_bytes[off:next_off] = blob off = next_off block_msg = InternalEthBlockInfo(block_msg_bytes) logger.debug( "Successfully parsed block broadcast message. {} " "transactions in block {}", tx_count, block_hash) bx_block_hash = convert.bytes_to_hex( crypto.double_sha256(bx_block_msg)) compressed_size = len(bx_block_msg) block_info = BlockInfo( block_hash, short_ids, decompress_start_datetime, datetime.datetime.utcnow(), (time.time() - decompress_start_timestamp) * 1000, tx_count, bx_block_hash, convert.bytes_to_hex(block_msg.prev_block_hash().binary), len(block_msg.rawbytes()), compressed_size, 100 - float(compressed_size) / content_size * 100, []) return BlockDecompressionResult(block_msg, block_info, unknown_tx_sids, unknown_tx_hashes) else: logger.debug( "Block recovery needed for {}. Missing {} sids, {} tx hashes. " "Total txs in block: {}", block_hash, len(unknown_tx_sids), len(unknown_tx_hashes), tx_count) return BlockDecompressionResult( None, BlockInfo(block_hash, short_ids, decompress_start_datetime, datetime.datetime.utcnow(), (time.time() - decompress_start_timestamp) * 1000, None, None, None, None, None, None, []), unknown_tx_sids, unknown_tx_hashes)
def block_to_bx_block( self, block_msg: InternalEthBlockInfo, tx_service: TransactionService) -> Tuple[memoryview, BlockInfo]: """ Convert Ethereum new block message to internal broadcast message with transactions replaced with short ids The code is optimized and does not make copies of bytes :param block_msg: Ethereum new block message :param tx_service: Transactions service :return: Internal broadcast message bytes (bytearray), tuple (txs count, previous block hash) """ compress_start_datetime = datetime.datetime.utcnow() compress_start_timestamp = time.time() msg_bytes = memoryview(block_msg.rawbytes()) _, block_msg_itm_len, block_msg_itm_start = rlp_utils.consume_length_prefix( msg_bytes, 0) block_msg_bytes = msg_bytes[block_msg_itm_start:block_msg_itm_start + block_msg_itm_len] _, block_hdr_itm_len, block_hdr_itm_start = rlp_utils.consume_length_prefix( block_msg_bytes, 0) block_hdr_full_bytes = block_msg_bytes[0:block_hdr_itm_start + block_hdr_itm_len] block_hdr_bytes = block_msg_bytes[ block_hdr_itm_start:block_hdr_itm_start + block_hdr_itm_len] _, prev_block_itm_len, prev_block_itm_start = rlp_utils.consume_length_prefix( block_hdr_bytes, 0) prev_block_bytes = block_hdr_bytes[ prev_block_itm_start:prev_block_itm_start + prev_block_itm_len] _, txs_itm_len, txs_itm_start = rlp_utils.consume_length_prefix( block_msg_bytes, block_hdr_itm_start + block_hdr_itm_len) txs_bytes = block_msg_bytes[txs_itm_start:txs_itm_start + txs_itm_len] remaining_bytes = block_msg_bytes[txs_itm_start + txs_itm_len:] used_short_ids = [] # creating transactions content content_size = 0 buf = deque() tx_start_index = 0 tx_count = 0 while True: if tx_start_index >= len(txs_bytes): break _, tx_item_length, tx_item_start = rlp_utils.consume_length_prefix( txs_bytes, tx_start_index) tx_bytes = txs_bytes[tx_start_index:tx_item_start + tx_item_length] tx_hash_bytes = eth_common_utils.keccak_hash(tx_bytes) tx_hash = Sha256Hash(tx_hash_bytes) short_id = tx_service.get_short_id(tx_hash) if short_id <= 0: is_full_tx_bytes = rlp_utils.encode_int(1) tx_content_bytes = tx_bytes else: is_full_tx_bytes = rlp_utils.encode_int(0) used_short_ids.append(short_id) tx_content_bytes = bytes() tx_content_prefix = rlp_utils.get_length_prefix_str( len(tx_content_bytes)) short_tx_content_size = len(is_full_tx_bytes) + len( tx_content_prefix) + len(tx_content_bytes) short_tx_content_prefix_bytes = rlp_utils.get_length_prefix_list( short_tx_content_size) buf.append(short_tx_content_prefix_bytes) buf.append(is_full_tx_bytes) buf.append(tx_content_prefix) buf.append(tx_content_bytes) content_size += len( short_tx_content_prefix_bytes) + short_tx_content_size tx_start_index = tx_item_start + tx_item_length tx_count += 1 list_of_txs_prefix_bytes = rlp_utils.get_length_prefix_list( content_size) buf.appendleft(list_of_txs_prefix_bytes) content_size += len(list_of_txs_prefix_bytes) buf.appendleft(block_hdr_full_bytes) content_size += len(block_hdr_full_bytes) buf.append(remaining_bytes) content_size += len(remaining_bytes) compact_block_msg_prefix = rlp_utils.get_length_prefix_list( content_size) buf.appendleft(compact_block_msg_prefix) content_size += len(compact_block_msg_prefix) short_ids_bytes = compact_block_short_ids_serializer.serialize_short_ids_into_bytes( used_short_ids) buf.append(short_ids_bytes) content_size += constants.UL_ULL_SIZE_IN_BYTES offset_buf = struct.pack("<Q", content_size) buf.appendleft(offset_buf) content_size += len(short_ids_bytes) # Parse it into the bloXroute message format and send it along block = bytearray(content_size) off = 0 for blob in buf: next_off = off + len(blob) block[off:next_off] = blob off = next_off bx_block_hash = convert.bytes_to_hex(crypto.double_sha256(block)) original_size = len(block_msg.rawbytes()) block_info = BlockInfo(block_msg.block_hash(), used_short_ids, compress_start_datetime, datetime.datetime.utcnow(), (time.time() - compress_start_timestamp) * 1000, tx_count, bx_block_hash, convert.bytes_to_hex(prev_block_bytes), original_size, content_size, 100 - float(content_size) / original_size * 100) return memoryview(block), block_info
def to_new_block_msg(self) -> NewBlockEthProtocolMessage: """ Converts message to instance of NewBlockEthProtocolMessage :return: instance of NewBlockEthProtocolMessage """ _, msg_itm_len, msg_itm_start = rlp_utils.consume_length_prefix( self._memory_view, 0) msg_itm_bytes = self._memory_view[msg_itm_start:] offset = 0 msg_size = 0 _, header_len, header_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) header_bytes = msg_itm_bytes[offset:header_start + header_len] offset = header_start + header_len msg_size += len(header_bytes) _, txs_len, txs_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) txs_bytes = msg_itm_bytes[offset:txs_start + txs_len] offset = txs_start + txs_len msg_size += len(txs_bytes) _, uncles_len, uncles_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) uncles_bytes = msg_itm_bytes[offset:uncles_start + uncles_len] offset = uncles_start + uncles_len msg_size += len(uncles_bytes) _, total_difficulty_len, total_difficulty_start = rlp_utils.consume_length_prefix( msg_itm_bytes, offset) total_difficulty_bytes = msg_itm_bytes[offset:total_difficulty_start + total_difficulty_len] msg_size += len(total_difficulty_bytes) block_prefix = rlp_utils.get_length_prefix_list( len(header_bytes) + len(txs_bytes) + len(uncles_bytes)) msg_size += len(block_prefix) msg_prefix = rlp_utils.get_length_prefix_list(msg_size) msg_size += len(msg_prefix) result_msg_bytes = bytearray(msg_size) written_bytes = 0 result_msg_bytes[written_bytes:written_bytes + len(msg_prefix)] = msg_prefix written_bytes += len(msg_prefix) result_msg_bytes[written_bytes:written_bytes + len(block_prefix)] = block_prefix written_bytes += len(block_prefix) result_msg_bytes[written_bytes:written_bytes + len(header_bytes)] = header_bytes written_bytes += len(header_bytes) result_msg_bytes[written_bytes:written_bytes + len(txs_bytes)] = txs_bytes written_bytes += len(txs_bytes) result_msg_bytes[written_bytes:written_bytes + len(uncles_bytes)] = uncles_bytes written_bytes += len(uncles_bytes) result_msg_bytes[written_bytes:written_bytes + len(total_difficulty_bytes)] = total_difficulty_bytes written_bytes += len(total_difficulty_bytes) assert written_bytes == msg_size return NewBlockEthProtocolMessage(result_msg_bytes)