def __add_genesis_block(self, tx_info: dict, reps: List[ExternalAddress]): """ :param tx_info: Transaction data for making genesis block from an initial file :return: """ logging.info("Make Genesis Block....") tx_builder = TransactionBuilder.new("genesis", self.tx_versioner) nid = tx_info.get("nid") if nid is not None: nid = int(nid, 16) tx_builder.nid = nid # Optional. It will be 0x3 except for mainnet and testnet if not defined tx_builder.accounts = tx_info["accounts"] tx_builder.message = tx_info["message"] tx = tx_builder.build() block_version = self.block_versioner.get_version(0) block_builder = BlockBuilder.new(block_version, self.tx_versioner) block_builder.height = 0 block_builder.fixed_timestamp = 0 block_builder.prev_hash = None block_builder.next_leader = ExternalAddress.fromhex(self.__peer_id) block_builder.transactions[tx.hash] = tx block_builder.reps = reps block = block_builder.build( ) # It does not have commit state. It will be rebuilt. block, invoke_results = ObjectManager().channel_service.genesis_invoke( block) self.set_invoke_results(block.header.hash.hex(), invoke_results) self.add_block(block)
def _tx_item(tx_versioner: TransactionVersioner) -> Transaction: test_signer = Signer.from_prikey(os.urandom(32)) tx_builder = TransactionBuilder.new("0x3", "", 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: Transaction = tx_builder.build() return tx
def test_add_remove_block_to_candidate_blocks(self): # GIVEN block0 = self.__get_test_block() block0.header.__dict__['height'] = -1 block = self.__get_test_block() blockchain = BlockChain('icon_dex', '', self) blockchain.__dict__['_BlockChain__last_block'] = block0 candidate_blocks = CandidateBlocks(blockchain) # WHEN add candidate_blocks.add_block(block, [ExternalAddress.empty()]) # THEN self.assertTrue(block.header.hash in candidate_blocks.blocks) # WHEN remove candidate_blocks.remove_block(block.header.hash) # THEN self.assertFalse(block.header.hash in candidate_blocks.blocks)
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) async with self.__lock: if self._block_manager.epoch.leader_id != ChannelProperty( ).peer_id: util.logger.warning( f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}" ) return self._vote_queue = asyncio.Queue(loop=self._loop) 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) need_next_call = False try: 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 """ self._made_block_count += 1 elif self.made_block_count >= (conf.MAX_MADE_BLOCK_COUNT - 1): if last_unconfirmed_block: await self.__add_block(last_unconfirmed_block) 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: util.logger.info( f"This leader already made {self.made_block_count} blocks. " f"MAX_MADE_BLOCK_COUNT is {conf.MAX_MADE_BLOCK_COUNT} " f"There is no more right. Consensus loop will return." ) return elif len(block_builder.transactions ) > 0 or conf.ALLOW_MAKE_EMPTY_BLOCK: if last_unconfirmed_block: next_leader = await self.__add_block_and_new_epoch( block_builder, last_unconfirmed_block) elif len(block_builder.transactions) == 0 and ( last_unconfirmed_block and len(last_unconfirmed_block.body.transactions) > 0): next_leader = await self.__add_block_and_new_epoch( block_builder, last_unconfirmed_block) else: need_next_call = True except NotEnoughVotes: need_next_call = True finally: if need_next_call: return self.__block_generation_timer.call() candidate_block = self.__build_candidate_block( block_builder, next_leader, vote_result) 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}") 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) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if await self._wait_for_voting(candidate_block) is None: return if 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})") ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) ObjectManager().channel_service.turn_on_leader_complain_timer() else: self._block_manager.epoch = Epoch.new_epoch( next_leader.hex_hx()) if not conf.ALLOW_MAKE_EMPTY_BLOCK: self.__block_generation_timer.call_instantly() else: self.__block_generation_timer.call()
def get_rep_ids(self) -> list: return [ ExternalAddress.fromhex_address(peer.get('id'), allow_malformed=True) for peer in self.get_channel_infos()['peers'] ]
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()
async def consensus(self): util.logger.debug( f"-------------------consensus " f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})" ) async with self.__lock: if self._block_manager.epoch.leader_id != ChannelProperty( ).peer_id: util.logger.warning( f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}" ) return self._vote_queue = asyncio.Queue(loop=self._loop) 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 """ 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 = await self._wait_for_voting( last_unconfirmed_block) if not vote_result: return self.__block_generation_timer.call() self.__add_block(last_unconfirmed_block, vote) self._block_manager.epoch = Epoch.new_epoch( ChannelProperty().peer_id) 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 = await self._wait_for_voting( last_unconfirmed_block) 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() candidate_block = self.__build_candidate_block( block_builder, next_leader, vote_result) 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}") 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) self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func) if await self._wait_for_voting(candidate_block) is None: return if len(candidate_block.body.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})") ObjectManager().channel_service.reset_leader( next_leader.hex_hx()) else: self._block_manager.epoch = Epoch.new_epoch( next_leader.hex_hx()) if not conf.ALLOW_MAKE_EMPTY_BLOCK: self.__block_generation_timer.call_instantly() 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)