async def _send_challenges_to_timelords( self, delivery: Delivery = Delivery.BROADCAST ) -> OutboundMessageGenerator: """ Sends all of the current heads (as well as Pos infos) to all timelord peers. """ challenge_requests: List[timelord_protocol.ChallengeStart] = [] pos_info_requests: List[timelord_protocol.ProofOfSpaceInfo] = [] async with self.store.lock: tips: List[HeaderBlock] = self.blockchain.get_current_tips() for tip in tips: assert tip.challenge challenge_hash = tip.challenge.get_hash() challenge_requests.append( timelord_protocol.ChallengeStart( challenge_hash, tip.challenge.total_weight)) tip_hashes = [tip.header_hash for tip in tips] tip_infos = [ tup[0] for tup in list(( await self.store.get_unfinished_blocks()).items()) if tup[1].prev_header_hash in tip_hashes ] for chall, iters in tip_infos: pos_info_requests.append( timelord_protocol.ProofOfSpaceInfo(chall, iters)) for challenge_msg in challenge_requests: yield OutboundMessage(NodeType.TIMELORD, Message("challenge_start", challenge_msg), delivery) for pos_info_msg in pos_info_requests: yield OutboundMessage( NodeType.TIMELORD, Message("proof_of_space_info", pos_info_msg), delivery, )
async def unfinished_block( self, unfinished_block: peer_protocol.UnfinishedBlock ) -> OutboundMessageGenerator: """ We have received an unfinished block, either created by us, or from another peer. We can validate it and if it's a good block, propagate it to other peers and timelords. """ # Adds the unfinished block to seen, and check if it's seen before if self.store.seen_unfinished_block( unfinished_block.block.header_hash): return if not self.blockchain.is_child_of_head(unfinished_block.block): return if not await self.blockchain.validate_unfinished_block( unfinished_block.block): raise InvalidUnfinishedBlock() prev_full_block: Optional[FullBlock] = await self.store.get_block( unfinished_block.block.prev_header_hash) assert prev_full_block prev_block: HeaderBlock = prev_full_block.header_block assert prev_block.challenge challenge_hash: bytes32 = prev_block.challenge.get_hash() difficulty: uint64 = self.blockchain.get_next_difficulty( unfinished_block.block.header_block.prev_header_hash) vdf_ips: uint64 = self.blockchain.get_next_ips( unfinished_block.block.header_block.prev_header_hash) iterations_needed: uint64 = calculate_iterations( unfinished_block.block.header_block.proof_of_space, difficulty, vdf_ips, constants["MIN_BLOCK_TIME"], ) if (await self.store.get_unfinished_block( (challenge_hash, iterations_needed)) is not None): return expected_time: uint64 = uint64( int(iterations_needed / (await self.store.get_proof_of_time_estimate_ips()))) if expected_time > constants["PROPAGATION_DELAY_THRESHOLD"]: log.info( f"Block is slow, expected {expected_time} seconds, waiting") # If this block is slow, sleep to allow faster blocks to come out first await asyncio.sleep(5) async with self.store.lock: leader: Tuple[uint32, uint64] = self.store.get_unfinished_block_leader() if leader is None or unfinished_block.block.height > leader[0]: log.info( f"This is the first unfinished block at height {unfinished_block.block.height}, so propagate." ) # If this is the first block we see at this height, propagate self.store.set_unfinished_block_leader( (unfinished_block.block.height, expected_time)) elif unfinished_block.block.height == leader[0]: if expected_time > leader[1] + constants[ "PROPAGATION_THRESHOLD"]: # If VDF is expected to finish X seconds later than the best, don't propagate log.info( f"VDF will finish too late {expected_time} seconds, so don't propagate" ) return elif expected_time < leader[1]: log.info( f"New best unfinished block at height {unfinished_block.block.height}" ) # If this will be the first block to finalize, update our leader self.store.set_unfinished_block_leader( (leader[0], expected_time)) else: # If we have seen an unfinished block at a greater or equal height, don't propagate log.info(f"Unfinished block at old height, so don't propagate") return await self.store.add_unfinished_block( (challenge_hash, iterations_needed), unfinished_block.block) timelord_request = timelord_protocol.ProofOfSpaceInfo( challenge_hash, iterations_needed) yield OutboundMessage( NodeType.TIMELORD, Message("proof_of_space_info", timelord_request), Delivery.BROADCAST, ) yield OutboundMessage( NodeType.FULL_NODE, Message("unfinished_block", unfinished_block), Delivery.BROADCAST_TO_OTHERS, )