async def integrate_block_with_existing_chain(self, block: Block, extra_blocks=None): """Even in case of retrace, this is the only place where we insert a new block into the block collection and update BU""" try: # TODO: reorg the checks, to have the faster ones first. # Like, here we begin with checking every tx one by one, when <e did not even check index and provided hash matched previous one. try: block.verify() except Exception as e: print("Integrate block error 1", e) return False for transaction in block.transactions: try: if extra_blocks: transaction.extra_blocks = extra_blocks transaction.verify() except InvalidTransactionException as e: print(e) return False except InvalidTransactionSignatureException as e: print(e) return False except MissingInputTransactionException as e: print(e) return False except NotEnoughMoneyException as e: print(e) return False except Exception as e: print(e) return False if block.index == 0: return True height = block.index last_block = self.existing_blockchain.blocks[block.index - 1] if last_block.index != (block.index - 1) or last_block.hash != block.prev_hash: print("Integrate block error 2") raise ForkException() if not last_block: print("Integrate block error 3") raise ForkException() target = BlockFactory.get_target(height, last_block, block, self.existing_blockchain) delta_t = int(time()) - int(last_block.time) special_target = CHAIN.special_target(block.index, block.target, delta_t, get_config().network) target_block_time = CHAIN.target_block_time(self.config.network) if block.index >= 35200 and delta_t < 600 and block.special_min: raise Exception('Special min block too soon') # TODO: use a CHAIN constant for pow blocks limits if ((int(block.hash, 16) < target) or (block.special_min and int(block.hash, 16) < special_target) or (block.special_min and block.index < 35200) or (block.index >= 35200 and block.index < 38600 and block.special_min and (int(block.time) - int(last_block.time)) > target_block_time) ): if last_block.index == ( block.index - 1) and last_block.hash == block.prev_hash: # self.mongo.db.blocks.update({'index': block.index}, block.to_dict(), upsert=True) # self.mongo.db.blocks.remove({'index': {"$gt": block.index}}, multi=True) # todo: is this useful? can we have more blocks above? No because if we had, we would have raised just above await self.mongo.async_db.block.delete_many( {'index': { "$gte": block.index }}) await self.mongo.async_db.blocks.replace_one( {'index': block.index}, block.to_dict(), upsert=True) # TODO: why do we need to keep that one in memory? try: self.existing_blockchain.blocks[block.index] = block del self.existing_blockchain.blocks[block.index + 1:] except: self.existing_blockchain.blocks.append(block) if self.debug: self.app_log.info( "New block inserted for height: {}".format( block.index)) await self.config.on_new_block( block) # This will propagate to BU return True else: print("Integrate block error 4") raise ForkException() else: print("Integrate block error 5") raise AboveTargetException() return False # unreachable code except Exception as e: exc_type, exc_obj, exc_tb = exc_info() fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1] self.app_log.warning( "integrate_block_with_existing_chain {} {} {}".format( exc_type, fname, exc_tb.tb_lineno)) raise
def get_target(cls, height, last_block, block, blockchain) -> int: try: # change target max_target = CHAIN.MAX_TARGET if get_config().network in ['regnet', 'testnet']: return int(max_target) max_block_time = CHAIN.target_block_time(get_config().network) retarget_period = CHAIN.RETARGET_PERIOD # blocks max_seconds = CHAIN.TWO_WEEKS # seconds min_seconds = CHAIN.HALF_WEEK # seconds if height >= CHAIN.POW_FORK_V3: retarget_period = CHAIN.RETARGET_PERIOD_V3 max_seconds = CHAIN.MAX_SECONDS_V3 # seconds min_seconds = CHAIN.MIN_SECONDS_V3 # seconds elif height >= CHAIN.POW_FORK_V2: retarget_period = CHAIN.RETARGET_PERIOD_V2 max_seconds = CHAIN.MAX_SECONDS_V2 # seconds min_seconds = CHAIN.MIN_SECONDS_V2 # seconds if height > 0 and height % retarget_period == 0: get_config().debug_log( "RETARGET get_target height {} - last_block {} - block {}/time {}" .format(height, last_block.index, block.index, block.time)) block_from_2016_ago = Block.from_dict( get_config().BU.get_block_by_index(height - retarget_period)) get_config().debug_log( "Block_from_2016_ago - block {}/time {}".format( block_from_2016_ago.index, block_from_2016_ago.time)) two_weeks_ago_time = block_from_2016_ago.time elapsed_time_from_2016_ago = int( last_block.time) - int(two_weeks_ago_time) get_config().debug_log( "elapsed_time_from_2016_ago {} s {} days".format( int(elapsed_time_from_2016_ago), elapsed_time_from_2016_ago / (60 * 60 * 24))) # greater than two weeks? if elapsed_time_from_2016_ago > max_seconds: time_for_target = max_seconds get_config().debug_log("gt max") elif elapsed_time_from_2016_ago < min_seconds: time_for_target = min_seconds get_config().debug_log("lt min") else: time_for_target = int(elapsed_time_from_2016_ago) block_to_check = last_block if blockchain.partial: start_index = len(blockchain.blocks) - 1 else: start_index = last_block.index get_config().debug_log("start_index {}".format(start_index)) while 1: if block_to_check.special_min or block_to_check.target == max_target or not block_to_check.target: block_to_check = blockchain.blocks[start_index] start_index -= 1 else: target = block_to_check.target break get_config().debug_log("start_index2 {}, target {}".format( block_to_check.index, hex(int(target))[2:].rjust(64, '0'))) new_target = int((time_for_target * target) / max_seconds) get_config().debug_log("new_target {}".format( hex(int(new_target))[2:].rjust(64, '0'))) if new_target > max_target: target = max_target else: target = new_target elif height == 0: target = max_target else: block_to_check = block delta_t = int(block.time) - int(last_block.time) if block.index >= 38600 and delta_t > max_block_time and block.special_min: special_target = CHAIN.special_target( block.index, block.target, delta_t, get_config().network) return special_target block_to_check = last_block # this would be accurate. right now, it checks if the current block is under its own target, not the previous block's target if blockchain.partial: start_index = len(blockchain.blocks) - 1 else: start_index = last_block.index while 1: if start_index == 0: return block_to_check.target if block_to_check.special_min or block_to_check.target == max_target or not block_to_check.target: block_to_check = blockchain.blocks[start_index] start_index -= 1 else: target = block_to_check.target break return int(target) except Exception as e: import sys, os print("Exception {} get_target".format(e)) exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) raise