async def announce_new_block(self, subscriber_block_height: int, subscriber_id: str): while True: my_block_height = self._blockchain.block_height if subscriber_block_height > my_block_height: logging.warning( f"subscriber's height({subscriber_block_height}) is higher " f"than this node's height({my_block_height}).") self._channel_service.inner_service.notify_unregister() error_msg = {"error": "Invalid block height from citizen."} return json.dumps(error_msg), b'' elif subscriber_block_height == my_block_height: async with self._citizen_condition_new_block: await self._citizen_condition_new_block.wait() new_block_height = subscriber_block_height + 1 new_block = self._blockchain.find_block_by_height(new_block_height) if new_block is None: logging.warning( f"Cannot find block height({new_block_height})") # To prevent excessive occupancy of the CPU in an infinite loop await asyncio.sleep(2 * conf.INTERVAL_BLOCKGENERATION) continue confirm_info: bytes = self._blockchain.find_confirm_info_by_hash( new_block.header.hash) logging.debug( f"announce_new_block: height({new_block.header.height}), to: {subscriber_id}" ) bs = BlockSerializer.new(new_block.header.version, self._blockchain.tx_versioner) return json.dumps(bs.serialize(new_block)), confirm_info
def __block_request_by_citizen(self, block_height): rs_client = ObjectManager().channel_service.rs_client get_block_result = rs_client.call( RestMethod.GetBlockByHeight, RestMethod.GetBlockByHeight.value.params(height=str(block_height))) last_block = rs_client.call(RestMethod.GetLastBlock) if not last_block: raise exception.InvalidBlockSyncTarget( "The Radiostation may not be ready. It will retry after a while." ) max_height = self.blockchain.block_versioner.get_height(last_block) block_version = self.blockchain.block_versioner.get_version( block_height) block_serializer = BlockSerializer.new(block_version, self.blockchain.tx_versioner) block = block_serializer.deserialize(get_block_result['block']) votes_dumped: str = get_block_result.get('confirm_info', '') try: votes_serialized = json.loads(votes_dumped) version = self.blockchain.block_versioner.get_version(block_height) votes = Votes.get_block_votes_class(version).deserialize_votes( votes_serialized) except json.JSONDecodeError: votes = votes_dumped return block, max_height, -1, votes, message_code.Response.success
async def node_ws_PublishNewBlock(self, **kwargs): block_dict, votes_dumped = kwargs.get('block'), kwargs.get('confirm_info', '') try: votes_serialized = json.loads(votes_dumped) vote = BlockVotes.deserialize_votes(votes_serialized) except json.JSONDecodeError: vote = votes_dumped blockchain = ObjectManager().channel_service.block_manager.blockchain new_block_height = blockchain.block_versioner.get_height(block_dict) if new_block_height > blockchain.block_height: block_version = blockchain.block_versioner.get_version(new_block_height) block_serializer = BlockSerializer.new(block_version, blockchain.tx_versioner) confirmed_block = block_serializer.deserialize(block_dict) block_verifier = BlockVerifier.new(block_version, blockchain.tx_versioner) block_verifier.invoke_func = blockchain.score_invoke reps_getter = blockchain.find_preps_addresses_by_roothash try: block_verifier.verify(confirmed_block, blockchain.last_block, blockchain, generator=blockchain.get_expected_generator(confirmed_block), reps_getter=reps_getter) except Exception as e: self._exception = AnnounceNewBlockError(f"error: {type(e)}, message: {str(e)}") else: logging.debug(f"add_confirmed_block height({confirmed_block.header.height}), " f"hash({confirmed_block.header.hash.hex()}), votes_dumped({votes_dumped})") ObjectManager().channel_service.block_manager.add_confirmed_block(confirmed_block=confirmed_block, confirm_info=vote) finally: ObjectManager().channel_service.reset_block_monitoring_timer()
async def get_block(self, block_height, block_hash, unconfirmed=False) -> Tuple[int, str, bytes, str]: block, block_hash, confirm_info, fail_response_code = ( await self.__get_block(block_hash, block_height, unconfirmed) ) if fail_response_code: return fail_response_code, block_hash, b"", json.dumps({}) tx_versioner = self._blockchain.tx_versioner bs = BlockSerializer.new(block.header.version, tx_versioner) block_dict = bs.serialize(block) return message_code.Response.success, block_hash, confirm_info, json.dumps(block_dict)
def test_verify(self, plyvel_db, block_versioner, tx_versioner): """ 1. prepare plyvel db, block_versioner, tx_versioner 2. pick block, transaction, vote, etc from db 3. verify block, vote transaction, vote, etc... """ # given db instance, block_versioner, tx_versioner block_key = plyvel_db.get(b'last_block_key') while True: # when get block from db block_dumped = plyvel_db.get(block_key) Logger.info(f"block_dump : {block_dumped}") block_serialized = json.loads(block_dumped) block_height = block_versioner.get_height(block_serialized) block_version = block_versioner.get_version(block_height) block_serializer = BlockSerializer.new(block_version, tx_versioner) block = block_serializer.deserialize(block_serialized) Logger.info(f"block_height : {block_height}, block_version : {block_version}") if block_height == 0: break # then block verify block_verifier = BlockVerifier.new(block_version, tx_versioner) block_verifier.verify_signature(block) # then vote verify if parse_version(block_version) >= parse_version("0.3"): Logger.info(f"leader_votes : {block.body.leader_votes}") for leader_vote in block.body.leader_votes: if not leader_vote: continue leader_vote.verify() Logger.info(f"prev_votes : {block.body.prev_votes}") for block_vote in block.body.prev_votes: if not block_vote: continue block_vote.verify() # then transaction verify for tx in block.body.transactions.values(): tv = TransactionVerifier.new(tx.version, tx.type(), tx_versioner) tv.verify_signature(tx) Logger.info(f"prev_hash : {block.header.prev_hash}, {bytes(block.header.prev_hash)}") block_key = block.header.prev_hash.hex().encode("utf-8")
async def get_block_v2(self, block_height, block_hash) -> Tuple[int, str, str]: # This is a temporary function for v2 support of exchanges. block, block_hash, _, fail_response_code = await self.__get_block( block_hash, block_height) if fail_response_code: return fail_response_code, block_hash, json.dumps({}) tx_versioner = self._blockchain.tx_versioner bs = BlockSerializer.new(block.header.version, tx_versioner) block_data_dict = bs.serialize(block) if block.header.height == 0: return message_code.Response.success, block_hash, json.dumps( block_data_dict) confirmed_tx_list_without_fail = [] for tx in block.body.transactions.values(): invoke_result = self._block_manager.get_invoke_result(tx.hash) if 'failure' in invoke_result: continue ts = TransactionSerializer.new(tx.version, tx.type(), tx_versioner) full_data = ts.to_full_data(tx) if tx.version == "0x3": step_used, step_price = int(invoke_result["stepUsed"], 16), int( invoke_result["stepPrice"], 16) full_data["fee"] = hex(step_used * step_price) confirmed_tx_list_without_fail.append(full_data) # Replace the existing confirmed_transactions with v2 ver. if block.header.version == "0.1a": block_data_dict[ "confirmed_transaction_list"] = confirmed_tx_list_without_fail else: block_data_dict["transactions"] = confirmed_tx_list_without_fail block_data_json = json.dumps(block_data_dict) if fail_response_code: return fail_response_code, block_hash, json.dumps({}) return message_code.Response.success, block_hash, block_data_json
async def _block_request_by_citizen( self, block_height: int, request_client: RestClient = None) -> RequestResult: max_height = self._max_height if request_client is None: request_client = self._rest_client get_block_result = await request_client.call_async( RestMethod.GetBlockByHeight, RestMethod.GetBlockByHeight.value.params(height=str(block_height))) response_code = get_block_result["response_code"] if response_code != message_code.Response.success: exc = exception.MessageCodeError( f"getBlockByHeight failed height={block_height}") exc.message_code = response_code raise exc if max_height == block_height: last_block_height = self._get_last_block_height() if last_block_height > max_height: max_height = last_block_height block_version = self._blockchain.block_versioner.get_version( block_height) block_serializer = BlockSerializer.new(block_version, self._blockchain.tx_versioner) block = block_serializer.deserialize(get_block_result['block']) votes_dumped: str = get_block_result.get('confirm_info', '') try: votes_serialized = json.loads(votes_dumped) version = self._blockchain.block_versioner.get_version( block_height) votes = Votes.get_block_votes_class(version).deserialize_votes( votes_serialized) except json.JSONDecodeError: votes = votes_dumped return block, max_height, -1, votes, message_code.Response.success
def test_block_v0_4(self): block_version = "0.4" test_signer = Signer.from_prikey(os.urandom(32)) tx_versioner = TransactionVersioner() dummy_receipts = {} block_builder = BlockBuilder.new(block_version, tx_versioner) for i in range(5): tx_builder = TransactionBuilder.new("0x3", None, tx_versioner) tx_builder.signer = test_signer tx_builder.to_address = ExternalAddress.new() tx_builder.step_limit = random.randint(0, 10000) tx_builder.value = random.randint(0, 10000) tx_builder.nid = 2 tx = tx_builder.build() tx_serializer = TransactionSerializer.new(tx.version, tx.type(), tx_versioner) block_builder.transactions[tx.hash] = tx dummy_receipts[tx.hash.hex()] = { "dummy_receipt": "dummy", "tx_dumped": tx_serializer.to_full_data(tx) } next_leader = ExternalAddress.fromhex( "hx00112233445566778899aabbccddeeff00112233") block_builder.signer = test_signer block_builder.height = 0 block_builder.prev_hash = Hash32(bytes(Hash32.size)) block_builder.state_hash = Hash32(bytes(Hash32.size)) block_builder.receipts = dummy_receipts block_builder.reps = [ ExternalAddress.fromhex_address(test_signer.address) ] block_builder.next_leader = next_leader block_builder.next_reps = [] vote = BlockVote.new(test_signer, utils.get_time_stamp(), block_builder.height - 1, 0, block_builder.prev_hash) votes = BlockVotes(block_builder.reps, conf.VOTING_RATIO, block_builder.height - 1, 0, block_builder.prev_hash) votes.add_vote(vote) block_builder.prev_votes = votes.votes block = block_builder.build() block_verifier = BlockVerifier.new(block_version, tx_versioner) block_verifier.invoke_func = lambda b, prev_b: (block, dummy_receipts) reps_getter = lambda _: block_builder.reps generator = ExternalAddress.fromhex_address(test_signer.address) block_verifier.verify(block, None, None, generator=generator, reps_getter=reps_getter) block_serializer = BlockSerializer.new(block_version, tx_versioner) block_serialized = block_serializer.serialize(block) logging.info(json.dumps(block_serialized, indent=4)) block_deserialized = block_serializer.deserialize(block_serialized) logging.info( json.dumps(block_serializer.serialize(block_deserialized), indent=4)) assert block.header == block_deserialized.header assert block.body == block_deserialized.body tx_hashes = list(block.body.transactions) tx_index = random.randrange(0, len(tx_hashes)) block_prover = BlockProver.new(block.header.version, tx_hashes, BlockProverType.Transaction) tx_proof = block_prover.get_proof(tx_index) assert block_prover.prove(tx_hashes[tx_index], block.header.transactions_hash, tx_proof) block_prover = BlockProver.new(block.header.version, block_builder.receipts, BlockProverType.Receipt) receipt_proof = block_prover.get_proof(tx_index) receipts_hash = block_prover.to_hash32( block_builder.receipts[tx_index]) assert block_prover.prove(receipts_hash, block.header.receipts_hash, receipt_proof)