class RoundIter: def __init__(self, dag, block_hash, round_type): block_number = dag.get_block_number(block_hash) epoch_number = Epoch.get_epoch_number(block_number) round_start, round_end = Epoch.get_round_bounds( epoch_number, round_type) self.round_end = round_start self.chain_iter = ChainIter(dag, block_hash) while self.chain_iter.block_number > round_end + 1: self.chain_iter.next() def __iter__(self): return self def __next__(self): block = self.chain_iter.next() block_number = self.chain_iter.block_number if block_number < self.round_end: raise StopIteration() return block def current_block_number(self): return self.chain_iter.block_number def next(self): return self.__next__()
def __init__(self, dag, block_hash, round_type): block_number = dag.get_block_number(block_hash) epoch_number = Epoch.get_epoch_number(block_number) round_start, round_end = Epoch.get_round_bounds( epoch_number, round_type) self.round_end = round_start self.chain_iter = ChainIter(dag, block_hash) while self.chain_iter.block_number > round_end + 1: self.chain_iter.next()
def flatten_with_merge(merger, from_hash, to_hash): flat_chain = [] chain_iter = ChainIter(merger.dag, from_hash) block = chain_iter.next() block_hash = block.get_hash() while block_hash != to_hash: if not block: flat_chain.append(None) else: flat_chain.append(block) if len(block.block.prev_hashes) > 1: merge_chain = merger.merge(block.block.prev_hashes) flat_chain += list(reversed(merge_chain)) chain_iter = ChainIter(merger.dag, merge_chain[0].get_hash()) chain_iter.next( ) # immediately transfer to next block because this one is already in chain block = chain_iter.next() if block: block_hash = block.get_hash() else: block_hash = None flat_chain.append(merger.dag.blocks_by_hash[to_hash]) return MergedChain(list(reversed(flat_chain)))
def calculate_zeta(self, block_hash): import sys max_zeta = -sys.maxsize - 1 # TODO replace with reasonable number tops = self.dag.get_top_blocks_hashes() for top in tops: zeta = 0 iterator = ChainIter(self.dag, top) consecutive_skips = 0 consecutive_blocks = 0 for block in iterator: if block: if block.get_hash() == block_hash: if max_zeta < zeta: max_zeta = zeta continue if block: consecutive_blocks += 1 consecutive_skips = 0 else: consecutive_skips += 1 consecutive_blocks = 0 if consecutive_skips == CONSECUTIVE_CONST: zeta -= 1 consecutive_skips = 0 elif consecutive_blocks == CONSECUTIVE_CONST: zeta += 1 consecutive_blocks = 0 return max_zeta
def get_stake_actions(self, epoch_hash): epoch_iter = ChainIter(self.epoch.dag, epoch_hash) stake_actions = [] count = 0 for block in epoch_iter: if epoch_iter.block_number == 0: break if block: for tx in block.block.system_txs: if isinstance(tx, StakeHoldTransaction) \ or isinstance(tx, StakeReleaseTransaction) \ or isinstance(tx, PenaltyTransaction) \ or isinstance(tx, PenaltyGossipTransaction): stake_actions.append(tx) count += 1 if count == Epoch.get_duration(): break stake_actions = list(reversed(stake_actions)) return stake_actions
def test_iterator(self): dag = Dag(0) private = Private.generate() block1 = BlockFactory.create_block_with_timestamp( [dag.genesis_block().get_hash()], BLOCK_TIME) signed_block1 = BlockFactory.sign_block(block1, private) dag.add_signed_block(1, signed_block1) block2 = BlockFactory.create_block_with_timestamp([block1.get_hash()], BLOCK_TIME * 2) signed_block2 = BlockFactory.sign_block(block2, private) dag.add_signed_block(2, signed_block2) block3 = BlockFactory.create_block_with_timestamp([block2.get_hash()], BLOCK_TIME * 3) signed_block3 = BlockFactory.sign_block(block3, private) dag.add_signed_block(3, signed_block3) # alternative chain other_block2 = BlockFactory.create_block_with_timestamp( [block1.get_hash()], BLOCK_TIME * 2 + 1) other_signed_block2 = BlockFactory.sign_block(other_block2, private) dag.add_signed_block(2, other_signed_block2) # intentionally skipped block # alternative chain other_block4 = BlockFactory.create_block_with_timestamp( [other_block2.get_hash()], BLOCK_TIME * 3 + 1) other_signed_block4 = BlockFactory.sign_block(other_block4, private) dag.add_signed_block(4, other_signed_block4) chain_iter = ChainIter(dag, block3.get_hash()) self.assertEqual(chain_iter.next().block.get_hash(), block3.get_hash()) self.assertEqual(chain_iter.next().block.get_hash(), block2.get_hash()) self.assertEqual(chain_iter.next().block.get_hash(), block1.get_hash()) chain_iter = ChainIter(dag, other_block4.get_hash()) self.assertEqual(chain_iter.next().block.get_hash(), other_block4.get_hash()) self.assertEqual(chain_iter.next(), None) # detect intentionally skipped block self.assertEqual(chain_iter.next().block.get_hash(), other_block2.get_hash()) self.assertEqual(chain_iter.next().block.get_hash(), block1.get_hash())
def find_epoch_hash_for_block(self, block_hash): chain_iter = ChainIter(self.dag, block_hash) just_take_next_non_skipped_block = False for block in chain_iter: if self.is_last_block_of_epoch( chain_iter.block_number ) or just_take_next_non_skipped_block: if block: return block.get_hash() else: just_take_next_non_skipped_block = True return None
def get_mutable_part_of_chain(self, top): chain_part = [] chain_iter = ChainIter(self.dag, top) count = 0 for block in chain_iter: count += 1 if count == ZETA: if block: chain_part.append(block) else: chain_part.append(None) return list(reversed(chain_part))
def get_top_chain_block_hashes(self, from_hash, to_hash): """ Method return list of logical block by block chain from hash to hash IN THE OPPOSITE DIRECTION (from current top to to_hash) :param from_hash: current top hash :param to_hash: any hash from chain :return: list of block hashes in logical sequences WITHOUT SKIPS """ flat_chain = [] chain_iter = ChainIter(self.dag, from_hash) block = chain_iter.next() block_hash = block.get_hash() while block_hash != to_hash: if block: flat_chain.append(block.get_hash()) block = chain_iter.next() if block: block_hash = block.get_hash() else: block_hash = None flat_chain.append(self.dag.blocks_by_hash[to_hash]) return list(reversed(flat_chain))
def calculate_confirmations(self, block_hash): confirmations = [0] block_number = self.dag.get_block_number(block_hash) tops = self.dag.get_branches_for_timeslot_range( block_number + 1, block_number + ZETA + 1) for top in tops: branch_confirmations = 0 chain_iter = ChainIter(self.dag, top) for block in chain_iter: if chain_iter.block_number == block_number: # if we counted enough if block.get_hash( ) == block_hash: # if we counted on the branch including target block confirmations.append(branch_confirmations) break if block: branch_confirmations += 1 return max(confirmations)