def test_valid_timestamp(self): """Test for timestamp buffer in block verifier""" def block_maker(timestamp: int, height: int = 0, prev_hash=None): """Make dummy block""" tx_versioner = TransactionVersioner() dummy_receipts = {} block_builder = BlockBuilder.new("0.1a", tx_versioner) for i in range(1000): 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) } block_builder.signer = test_signer block_builder.prev_hash = prev_hash block_builder.height = height block_builder.state_hash = Hash32(bytes(Hash32.size)) block_builder.receipts = dummy_receipts block_builder.reps = [ExternalAddress.fromhex_address(test_signer.address)] block_builder.peer_id = ExternalAddress.fromhex(test_signer.address) block_builder.next_leader = ExternalAddress.fromhex(test_signer.address) block_builder.fixed_timestamp = timestamp b = block_builder.build() assert b.header.timestamp == timestamp return b test_signer = Signer.from_prikey(os.urandom(32)) first_block = block_maker(height=0, timestamp=utils.get_time_stamp()) second_block = block_maker(height=1, timestamp=utils.get_time_stamp() + 5, prev_hash=first_block.header.hash) third_block_from_far_future = block_maker(height=2, prev_hash=second_block.header.hash, timestamp=utils.get_time_stamp() + conf.TIMESTAMP_BUFFER_IN_VERIFIER + 5_000_000) block_verifier = BlockVerifier.new("0.1a", TransactionVersioner()) leader = first_block.header.peer_id reps = [ExternalAddress.fromhex_address(test_signer.address)] print("*---Normal time range") block_verifier.verify(block=second_block, prev_block=first_block, blockchain=None, generator=leader, reps=reps) print("*---Abnormal time range") with self.assertRaises(Exception): block_verifier.verify(block=third_block_from_far_future, prev_block=second_block, blockchain=None, generator=leader, reps=reps)
def generate_block(self, prev_block=None): """블럭을 생성한다 \n 이전블럭을 입력하지 않으면, 제네시스 블럭으로 생성됨 이전블럭을 입력하면 링킹된 블럭으로 생성됨 블럭 높이와 이전 블럭 hash, 현재블럭의 hash계산, 머클트리 계산을 실행함 :param prev_block: 이전 블럭 :returns: 생성된 블럭 해쉬 값 """ if prev_block is None: # 제네시스 블럭일 경우 # Genesis Block Data self.prev_block_hash = "" self.height = 0 self.time_stamp = 0 elif self.time_stamp == 0: # 블럭생성이 시작되지 않았으면 if self.prev_block_hash == "": self.prev_block_hash = prev_block.block_hash self.height = prev_block.height + 1 self.time_stamp = util.get_time_stamp() # ms단위 # 트랜잭션이 있을 경우 머클트리 생성 if len(self.confirmed_transaction_list) > 0: self.__calculate_merkle_tree_root_hash() self.block_hash = self.__generate_hash() return self.block_hash
def verify_prev_block(self, block: 'Block', prev_block: 'Block'): if block.header.height != prev_block.header.height + 1: exception = BlockHeightMismatch( f"Block({block.header.height}, {block.header.hash.hex()}, " f"Height({block.header.height}), " f"Expected({prev_block.header.height + 1}).") self._handle_exception(exception) if block.header.prev_hash != prev_block.header.hash: exception = RuntimeError( f"Block({block.header.height}, {block.header.hash.hex()}, " f"PrevHash({block.header.prev_hash.hex()}), " f"Expected({prev_block.header.hash.hex()}).") self._handle_exception(exception) valid_max_timestamp = utils.get_time_stamp( ) + conf.TIMESTAMP_BUFFER_IN_VERIFIER if prev_block and not (prev_block.header.timestamp < block.header.timestamp < valid_max_timestamp): exception = RuntimeError( f"Block({block.header.height}, {block.header.hash.hex()}," f"timestamp({block.header.timestamp} is invalid. " f"prev_block timestamp({prev_block.header.timestamp}), " f"current timestamp({utils.get_now_time_stamp()}") self._handle_exception(exception)
def leader_complain(self): complained_leader_id, new_leader_id = self.get_leader_ids_for_complaint( ) version = self.blockchain.block_versioner.get_version( self.epoch.height) leader_vote = Vote.get_leader_vote_class(version).new( signer=ChannelProperty().peer_auth, block_height=self.epoch.height, round_=self.epoch.round, old_leader=ExternalAddress.fromhex_address(complained_leader_id), new_leader=ExternalAddress.fromhex_address(new_leader_id), timestamp=util.get_time_stamp()) util.logger.info( f"LeaderVote : old_leader({complained_leader_id}), new_leader({new_leader_id}), round({self.epoch.round})" ) self.add_complain(leader_vote) leader_vote_serialized = leader_vote.serialize() leader_vote_dumped = json.dumps(leader_vote_serialized) request = loopchain_pb2.ComplainLeaderRequest( complain_vote=leader_vote_dumped, channel=self.channel_name) util.logger.debug(f"leader complain " f"complained_leader_id({complained_leader_id}), " f"new_leader_id({new_leader_id})") reps_hash = self.blockchain.get_next_reps_hash_by_header( self.blockchain.last_block.header) self.__channel_service.broadcast_scheduler.schedule_broadcast( "ComplainLeader", request, reps_hash=reps_hash)
def __send_fail_leader_vote(self, leader_vote: LeaderVote): version = self.blockchain.block_versioner.get_version( leader_vote.block_height) fail_vote = Vote.get_leader_vote_class(version).new( signer=ChannelProperty().peer_auth, block_height=leader_vote.block_height, round_=leader_vote.round, old_leader=leader_vote.old_leader, new_leader=ExternalAddress.empty(), timestamp=util.get_time_stamp()) fail_vote_dumped = json.dumps(fail_vote.serialize()) request = loopchain_pb2.ComplainLeaderRequest( complain_vote=fail_vote_dumped, channel=self.channel_name) reps_hash = self.blockchain.last_block.header.revealed_next_reps_hash or ChannelProperty( ).crep_root_hash rep_id = leader_vote.rep.hex_hx() target = self.blockchain.find_preps_targets_by_roothash( reps_hash)[rep_id] util.logger.debug(f"fail leader complain " f"complained_leader_id({leader_vote.old_leader}), " f"new_leader_id({ExternalAddress.empty()})," f"round({leader_vote.round})," f"target({target})") self.__channel_service.broadcast_scheduler.schedule_send_failed_leader_complain( "ComplainLeader", request, target=target)
def verify_common(self, block: 'Block', prev_block: 'Block', generator: 'ExternalAddress'=None, **kwargs): header: BlockHeader = block.header if header.timestamp is None: exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} does not have timestamp.") self._handle_exception(exception) if header.height > 0 and header.prev_hash is None: exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} does not have prev_hash.") self._handle_exception(exception) if prev_block and not (prev_block.header.timestamp < header.timestamp < utils.get_time_stamp()): exception = RuntimeError(f"Block({header.height}, {header.hash.hex()} timestamp({header.timestamp} is invalid. " f"prev_block timestamp({prev_block.header.timestamp}), " f"current timestamp({utils.get_now_time_stamp()}") self._handle_exception(exception) self.verify_version(block) if block.header.height > 0: self.verify_signature(block) if prev_block: self.verify_prev_block(block, prev_block) return self._verify_common(block, prev_block, generator, **kwargs)
def generate_block(self, prev_block=None): """블럭을 생성한다 \n 이전블럭을 입력하지 않으면, 제네시스 블럭으로 생성됨 이전블럭을 입력하면 링킹된 블럭으로 생성됨 블럭 높이와 이전 블럭 hash, 현재블럭의 hash계산, 머클트리 계산을 실행함 :param prev_block: 이전 블럭 :returns: 생성된 블럭 해쉬 값 """ try: util.logger.spam( f"ENGINE-303 generate_block prev_block: {prev_block.height} {prev_block.block_hash}" ) except Exception: pass if prev_block is None: # Genesis Block Data self.prev_block_hash = "" self.height = 0 self.time_stamp = 0 elif self.time_stamp == 0: if self.prev_block_hash == "": self.prev_block_hash = prev_block.block_hash self.height = prev_block.height + 1 self.time_stamp = util.get_time_stamp() # 트랜잭션이 있을 경우 머클트리 생성 if self.confirmed_tx_len > 0: Block.__calculate_merkle_tree_root_hash(self) self.block_hash = Block.__generate_hash(self) return self.block_hash
def vote_unconfirmed_block(self, block: Block, round_: int, is_validated): util.logger.debug( f"vote_unconfirmed_block() ({block.header.height}/{block.header.hash}/{is_validated})" ) vote = Vote.get_block_vote_class(block.header.version).new( signer=ChannelProperty().peer_auth, block_height=block.header.height, round_=round_, block_hash=block.header.hash if is_validated else Hash32.empty(), timestamp=util.get_time_stamp()) self.candidate_blocks.add_vote(vote) vote_serialized = vote.serialize() vote_dumped = json.dumps(vote_serialized) block_vote = loopchain_pb2.BlockVote(vote=vote_dumped, channel=ChannelProperty().name) target_reps_hash = block.header.reps_hash if not target_reps_hash: target_reps_hash = self.__channel_service.peer_manager.crep_root_hash self.__channel_service.broadcast_scheduler.schedule_broadcast( "VoteUnconfirmedBlock", block_vote, reps_hash=target_reps_hash) return vote
def __init__(self, block_hash: Hash32, block_height: int): """Recommend use factory methods(from_*) instead direct this. """ self.votes: dict[int, BlockVotes] = {} self.votes_buffer: List[BlockVote] = [] self.start_time = util.get_time_stamp() # timestamp self.hash = block_hash self.height = block_height self.__block = None self._reps: List[ExternalAddress] = []
def __init__(self, block_hash: Hash32): """Recommend use factory methods(from_*) instead direct this. """ if ObjectManager().channel_service: audience = ObjectManager().channel_service.peer_manager else: audience = None self.start_time = util.get_time_stamp() # timestamp self.hash = block_hash self.vote = Vote(block_hash.hex(), audience) self.__block = None
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)