def test_fail_vote(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer( PeerInfo("peerid-4", "groupid-3", "peerid-4_target", cert=self.__cert)) peer_manager.add_peer( PeerInfo("peerid-5", "groupid-3", "peerid-5_target", cert=self.__cert)) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-4", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-5", conf.TEST_FAIL_VOTE_SIGN) vote.get_result("block_hash", 0.51) # THEN self.assertTrue(vote.is_failed_vote("block_hash", 0.51))
def test_add_vote_fail_before_add_peer(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer( PeerInfo("peerid-4", "groupid-3", "peerid-4_target", cert=self.__cert)) peer_manager.add_peer( PeerInfo("peerid-5", "groupid-3", "peerid-5_target", cert=self.__cert)) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", None) vote.add_vote("groupid-3", "peerid-4", None) ret1 = vote.add_vote("groupid-4", "peerid-1", None) ret2 = vote.add_vote("groupid-1", "peerid-9", None) self.assertFalse(ret1) self.assertFalse(ret2) # THEN ret = vote.get_result_detail("block_hash", 0.51) self.assertEqual(ret[5], 5)
def add_unconfirmed_block(self, block): """Block Manager 가 주기적으로 생성한 블럭을 등록한다. 이 블럭은 각 Peer 로 전송되어 Validate vote 를 받아야 한다. :param block: Block Manager 가 tx 를 수집하여 주기적으로 생성한 블럭, 아직 Block Chain 의 멤버가 아니다. :return: unconfirmed block 을 식별하기 위한 block_hash (str) """ # block 생성자의 peer_id 를 지정한다. (새로 네트워크에 참여하는 피어는 마지막 블럭의 peer_id 를 리더로 간주한다.) block.peer_id = self.__peer_id # leader 가 block 에 담을 때 이미 1 투표한 내용으로 생성한다. vote = Vote(block.block_hash, ObjectManager().peer_service.peer_list) vote.add_vote(ObjectManager().peer_service.group_id, ObjectManager().peer_service.peer_id, None) self.__unconfirmed_blocks[block.block_hash] = [vote, block] self.__candidate_last_block = block return block.block_hash
def test_add_vote(self): # GIVEN peer_manager = PeerManager() self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer(PeerInfo("peerid-4", "groupid-3", "peerid-4_target", cert=self.__cert)) peer_manager.add_peer(PeerInfo("peerid-5", "groupid-3", "peerid-5_target", cert=self.__cert)) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", None) self.assertFalse(vote.get_result("block_hash", 0.51)) # THEN vote.add_vote("groupid-2", "peerid-2", None) self.assertTrue(vote.get_result("block_hash", 0.51))
def test_fail_vote(self): # GIVEN peer_manager = PeerManager() peer_manager.add_peer("peerid-1", "groupid-1", "peerid-1_target") peer_manager.add_peer("peerid-2", "groupid-2", "peerid-2_target") peer_manager.add_peer("peerid-3", "groupid-3", "peerid-3_target") peer_manager.add_peer("peerid-4", "groupid-3", "peerid-4_target") peer_manager.add_peer("peerid-5", "groupid-3", "peerid-5_target") vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-4", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-5", conf.TEST_FAIL_VOTE_SIGN) vote.get_result("block_hash", 0.51) # THEN self.assertTrue(vote.is_failed_vote("block_hash", 0.51))
def test_add_vote_fail_before_add_peer(self): # GIVEN peer_manager = PeerManager() peer_manager.add_peer("peerid-1", "groupid-1", "peerid-1_target") peer_manager.add_peer("peerid-2", "groupid-2", "peerid-2_target") peer_manager.add_peer("peerid-3", "groupid-3", "peerid-3_target") peer_manager.add_peer("peerid-4", "groupid-3", "peerid-4_target") peer_manager.add_peer("peerid-5", "groupid-3", "peerid-5_target") vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", None) vote.add_vote("groupid-3", "peerid-4", None) ret1 = vote.add_vote("groupid-4", "peerid-1", None) ret2 = vote.add_vote("groupid-1", "peerid-9", None) self.assertFalse(ret1) self.assertFalse(ret2) # THEN ret = vote.get_result_detail("block_hash", 0.51) self.assertEqual(ret[5], 5)
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)