async def _vote(self, unconfirmed_block: Block): exc = None try: block_version = self.__blockchain.block_versioner.get_version( unconfirmed_block.header.height) block_verifier = BlockVerifier.new(block_version, self.__blockchain.tx_versioner) block_verifier.invoke_func = self.__channel_service.score_invoke reps = self.__channel_service.get_rep_ids() logging.debug( f"unconfirmed_block.header({unconfirmed_block.header})") invoke_results = block_verifier.verify( unconfirmed_block, self.__blockchain.last_block, self.__blockchain, self.__blockchain.last_block.header.next_leader, reps=reps) except Exception as e: exc = e logging.error(e) traceback.print_exc() else: self.set_invoke_results(unconfirmed_block.header.hash.hex(), invoke_results) self.candidate_blocks.add_block(unconfirmed_block) finally: self.vote_unconfirmed_block(unconfirmed_block.header.hash, exc is None)
def __confirm_prev_block_by_sync(self, block_): prev_block = self.__blockchain.last_unconfirmed_block confirm_info = block_.body.confirm_prev_block logging.debug( f"block_manager.py >> block_height_sync :: height({prev_block.header.height})" ) block_version = self.get_blockchain().block_versioner.get_version( prev_block.header.height) block_verifier = BlockVerifier.new(block_version, self.get_blockchain().tx_versioner) if prev_block.header.height == 0: block_verifier.invoke_func = self.__channel_service.genesis_invoke else: block_verifier.invoke_func = self.__channel_service.score_invoke reps = self.__channel_service.get_rep_ids() invoke_results = block_verifier.verify_loosely( prev_block, self.__blockchain.last_block, self.__blockchain, reps=reps) self.__blockchain.set_invoke_results(prev_block.header.hash.hex(), invoke_results) return self.__blockchain.add_block(prev_block, confirm_info)
async def node_ws_PublishNewBlock(self, **kwargs): if 'error' in kwargs: if kwargs.get('code') in CONNECTION_FAIL_CONDITIONS: self._exception = ConnectionError(kwargs['error']) return else: return ObjectManager().channel_service.shutdown_peer( message=kwargs.get('error')) block_dict, confirm_info_str = kwargs.get('block'), kwargs.get( 'confirm_info') confirm_info = confirm_info_str.encode( "utf-8") if confirm_info_str else None blockchain = ObjectManager( ).channel_service.block_manager.get_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 = ObjectManager( ).channel_service.score_invoke reps = ObjectManager().channel_service.get_rep_ids() try: block_verifier.verify(confirmed_block, blockchain.last_block, blockchain, blockchain.last_block.header.next_leader, reps=reps) 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()}), confirm_info({confirm_info})" ) ObjectManager( ).channel_service.block_manager.add_confirmed_block( confirmed_block=confirmed_block, confirm_info=confirm_info)
def vote_as_peer(self): """Vote to AnnounceUnconfirmedBlock """ if self.__unconfirmedBlockQueue.empty(): return unconfirmed_block: Block = self.__unconfirmedBlockQueue.get() logging.debug( f"we got unconfirmed block ....{unconfirmed_block.header.hash.hex()}" ) my_height = self.__blockchain.block_height if my_height < (unconfirmed_block.header.height - 1): self.__channel_service.state_machine.block_sync() return # a block is already added that same height unconfirmed_block height if my_height >= unconfirmed_block.header.height: return logging.info("PeerService received unconfirmed block: " + unconfirmed_block.header.hash.hex()) block_version = self.__blockchain.block_versioner.get_version( unconfirmed_block.header.height) block_verifier = BlockVerifier.new(block_version, self.__blockchain.tx_versioner) block_verifier.invoke_func = self.__channel_service.score_invoke exception = None try: invoke_results = block_verifier.verify( unconfirmed_block, self.__blockchain.last_block, self.__blockchain, self.__blockchain.last_block.header.next_leader) except Exception as e: exception = e logging.error(e) traceback.print_exc() else: self.set_invoke_results(unconfirmed_block.header.hash.hex(), invoke_results) self.candidate_blocks.add_block(unconfirmed_block) finally: self.vote_unconfirmed_block(unconfirmed_block.header.hash, exception is None)
def __add_block_by_sync(self, block_): commit_state = block_.header.commit_state logging.debug( f"block_manager.py >> block_height_sync :: " f"height({block_.header.height}) commit_state({commit_state})") block_version = self.get_blockchain().block_versioner.get_version( block_.header.height) block_verifier = BlockVerifier.new(block_version, self.get_blockchain().tx_versioner) if block_.header.height == 0: block_verifier.invoke_func = self.__channel_service.genesis_invoke else: block_verifier.invoke_func = self.__channel_service.score_invoke invoke_results = block_verifier.verify_loosely( block_, self.__blockchain.last_block, self.__blockchain) self.__blockchain.set_invoke_results(block_.header.hash.hex(), invoke_results) return self.add_block(block_)
def __add_block_by_sync(self, block_, confirm_info=None): logging.debug( f"block_manager.py >> block_height_sync :: " f"height({block_.header.height}) confirm_info({confirm_info})") block_version = self.get_blockchain().block_versioner.get_version( block_.header.height) block_verifier = BlockVerifier.new(block_version, self.get_blockchain().tx_versioner, raise_exceptions=False) if block_.header.height == 0: block_verifier.invoke_func = self.__channel_service.genesis_invoke else: block_verifier.invoke_func = self.__channel_service.score_invoke reps = self.__channel_service.get_rep_ids() invoke_results = block_verifier.verify_loosely( block_, self.__blockchain.last_block, self.__blockchain, reps=reps) need_to_write_tx_info, need_to_score_invoke = True, True for exc in block_verifier.exceptions: if isinstance(exc, TransactionInvalidDuplicatedHash): need_to_write_tx_info = False if isinstance(exc, ScoreInvokeError) and not need_to_write_tx_info: need_to_score_invoke = False exc = next((exc for exc in block_verifier.exceptions if not isinstance(exc, TransactionInvalidDuplicatedHash)), None) if exc: if isinstance(exc, ScoreInvokeError) and not need_to_score_invoke: pass else: raise exc self.__blockchain.set_invoke_results(block_.header.hash.hex(), invoke_results) return self.__blockchain.add_block(block_, confirm_info, need_to_write_tx_info, need_to_score_invoke)
async def consensus(self): start_time = time.time() empty_block: Block = None try: self._loop = asyncio.get_event_loop() self._vote_queue = asyncio.Queue(loop=self._loop) block_builder = self._makeup_block() if len(block_builder.transactions ) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK: return peer_manager = ObjectManager().channel_service.peer_manager last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = Address.fromhex( peer_manager.get_next_leader_peer().peer_id) block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = (self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) block_verifier = BlockVerifier.new("0.1a") block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) logging.info( f"candidate block height: {candidate_block.header.height}") logging.info( f"candidate block hash: {candidate_block.header.hash.hex()}") logging.info( f"candidate block next leader: {candidate_block.header.next_leader.hex()}" ) logging.info( f"candidate block confirm_prev_block: {candidate_block.body.confirm_prev_block}" ) vote = Vote(candidate_block.header.hash.hex(), ObjectManager().channel_service.peer_manager) vote.add_vote(ChannelProperty().group_id, ChannelProperty().peer_id, True) self._blockmanager.broadcast_send_unconfirmed_block( candidate_block) success = await self._wait_for_voting(candidate_block, vote) if not success: return self._blockmanager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) self._blockmanager.add_block(candidate_block) self._made_block_count += 1 pending_tx = self._txQueue.get_item_in_status( TransactionStatusInQueue.normal, TransactionStatusInQueue.normal) if not pending_tx and not conf.ALLOW_MAKE_EMPTY_BLOCK: block_builder = BlockBuilder.new("0.1a") block_builder.prev_hash = candidate_block.header.hash block_builder.height = candidate_block.header.height + 1 block_builder.next_leader = candidate_block.header.next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = True empty_block = block_builder.build() self._blockmanager.broadcast_send_unconfirmed_block( empty_block) ObjectManager().channel_service.state_machine.turn_to_peer() finally: if not empty_block: elapsed_time = time.time() - start_time delay_time = conf.INTERVAL_BLOCKGENERATION - elapsed_time self._start_consensus_timer(delay_time)
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._blockmanager.candidate_blocks.blocks)})" ) with self.__lock: block_builder = self._makeup_block() vote_result = None if len(block_builder.transactions) > 0: # util.logger.debug(f"-------------------consensus logic-1") next_leader = ExternalAddress.fromhex( ChannelProperty().peer_id) if self._blockchain.last_unconfirmed_block: if (len(self._blockchain.last_unconfirmed_block.body. transactions) > 0) or ( len(self._blockchain.last_unconfirmed_block. body.transactions) == 0 and self._blockchain.last_unconfirmed_block.header. peer_id.hex_hx() != ChannelProperty().peer_id): # util.logger.debug(f"-------------------consensus logic-2") vote = self._blockmanager.candidate_blocks.get_vote( self._blockchain.last_unconfirmed_block.header.hash ) vote_result = vote.get_result( self._blockchain.last_unconfirmed_block.header. hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self._blockmanager.add_block( self._blockchain.last_unconfirmed_block, vote) self._made_block_count += 1 next_leader = self._blockchain.last_unconfirmed_block.header.next_leader else: if self._blockchain.last_unconfirmed_block and len( self._blockchain.last_unconfirmed_block.body. transactions) > 0: # util.logger.debug(f"-------------------consensus logic-3") vote = self._blockmanager.candidate_blocks.get_vote( self._blockchain.last_unconfirmed_block.header.hash) vote_result = vote.get_result( self._blockchain.last_unconfirmed_block.header.hash. hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self._blockmanager.add_block( self._blockchain.last_unconfirmed_block, vote) self._made_block_count += 1 peer_manager = ObjectManager().channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer().peer_id) else: # util.logger.spam(f"tx count in block({len(block_builder.transactions)})") return self.__block_generation_timer.call() last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.peer_private_key block_builder.confirm_prev_block = vote_result or ( self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._blockmanager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) block_verifier = BlockVerifier.new(candidate_block.header.version, self._blockchain.tx_versioner) block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) logging.debug(f"candidate block : {candidate_block.header}") self._blockmanager.vote_unconfirmed_block( candidate_block.header.hash, True) self._blockmanager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._blockmanager.broadcast_send_unconfirmed_block, candidate_block) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \ next_leader.hex() != ChannelProperty().peer_id: # util.logger.debug(f"-------------------turn_to_peer") ObjectManager().channel_service.state_machine.turn_to_peer() else: self.__block_generation_timer.call()
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) with self.__lock: complained_result = self._block_manager.epoch.complained_result block_builder = self._block_manager.epoch.makeup_block( complained_result) vote_result = None last_unconfirmed_block = self._blockchain.last_unconfirmed_block next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id) if complained_result: util.logger.spam("consensus block_builder.complained") confirm_info = self._blockchain.find_confirm_info_by_hash( self._blockchain.last_block.header.hash) if not confirm_info and self._blockchain.last_block.header.height > 0: util.logger.spam( "Can't make a block as a leader, this peer will be complained too." ) return vote_result = True self._block_manager.epoch.set_epoch_leader( ChannelProperty().peer_id) self._made_block_count += 1 elif len(block_builder.transactions) > 0: util.logger.spam( f"consensus len(block_builder.transactions) > 0") if last_unconfirmed_block: if (len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained) or ( len(last_unconfirmed_block.body.transactions) == 0 and last_unconfirmed_block.header.peer_id.hex_hx() != ChannelProperty().peer_id): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = vote.get_result( last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) next_leader = last_unconfirmed_block.header.next_leader else: if (last_unconfirmed_block) and ( len(last_unconfirmed_block.body.transactions) > 0 or last_unconfirmed_block.header.complained): vote = self._block_manager.candidate_blocks.get_vote( last_unconfirmed_block.header.hash) vote_result = vote.get_result( last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) peer_manager = ObjectManager().channel_service.peer_manager next_leader = ExternalAddress.fromhex( peer_manager.get_next_leader_peer( current_leader_peer_id=ChannelProperty().peer_id). peer_id) else: return self.__block_generation_timer.call() last_block = self._blockchain.last_block block_builder.height = last_block.header.height + 1 block_builder.prev_hash = last_block.header.hash block_builder.next_leader = next_leader block_builder.peer_private_key = ObjectManager( ).channel_service.peer_auth.private_key block_builder.confirm_prev_block = vote_result or ( self._made_block_count > 0) candidate_block = block_builder.build() candidate_block, invoke_results = ObjectManager( ).channel_service.score_invoke(candidate_block) self._block_manager.set_invoke_results( candidate_block.header.hash.hex(), invoke_results) util.logger.spam(f"candidate block : {candidate_block.header}") block_verifier = BlockVerifier.new(candidate_block.header.version, self._blockchain.tx_versioner) block_verifier.verify(candidate_block, self._blockchain.last_block, self._blockchain) self._block_manager.vote_unconfirmed_block( candidate_block.header.hash, True) self._block_manager.candidate_blocks.add_block(candidate_block) self._blockchain.last_unconfirmed_block = candidate_block broadcast_func = partial( self._block_manager.broadcast_send_unconfirmed_block, candidate_block) # TODO Temporary ignore below line for developing leader complain self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \ next_leader.hex_hx() != ChannelProperty().peer_id: util.logger.spam(f"-------------------turn_to_peer " f"next_leader({next_leader.hex_hx()}) " f"peer_id({ChannelProperty().peer_id})") await ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) else: self.__block_generation_timer.call()
def test_block_v0_3(self): private_auth = test_util.create_default_peer_auth() tx_versioner = TransactionVersioner() dummy_receipts = {} block_builder = BlockBuilder.new("0.3", tx_versioner) for i in range(1000): tx_builder = TransactionBuilder.new("0x3", tx_versioner) tx_builder.private_key = private_auth.private_key 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_versioner) block_builder.transactions[tx.hash] = tx dummy_receipts[tx.hash.hex()] = { "dummy_receipt": "dummy", "tx_dumped": tx_serializer.to_full_data(tx) } block_builder.peer_private_key = private_auth.private_key block_builder.height = 0 block_builder.state_hash = Hash32(bytes(Hash32.size)) block_builder.receipts = dummy_receipts block_builder.reps = [ ExternalAddress.fromhex_address(private_auth.address) ] block_builder.next_leader = ExternalAddress.fromhex( "hx00112233445566778899aabbccddeeff00112233") block = block_builder.build() block_verifier = BlockVerifier.new("0.3", tx_versioner) block_verifier.invoke_func = lambda b: (block, dummy_receipts) block_verifier.verify(block, None, None, block.header.peer_id, reps=block_builder.reps) block_serializer = BlockSerializer.new("0.3", tx_versioner) block_serialized = block_serializer.serialize(block) block_deserialized = block_serializer.deserialize(block_serialized) assert block.header == block_deserialized.header # FIXME : confirm_prev_block not serialized # 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.transaction_hash, tx_proof) block_prover = BlockProver.new(block.header.version, block_builder.receipts, BlockProverType.Receipt) receipt_proof = block_prover.get_proof(tx_index) receipt_hash = block_prover.to_hash32(block_builder.receipts[tx_index]) assert block_prover.prove(receipt_hash, block.header.receipt_hash, receipt_proof)