def try_add_stolen_flag(flag: models.Flag, attacker: int, current_round: int) -> bool: """ Flag validation function. Checks that flag is valid for current round, adds it to cache for team, atomically checking if it's already submitted by the attacker. :param flag: Flag model instance :param attacker: attacker team id :param current_round: current round """ stolen_key = CacheKeys.team_stolen_flags(attacker) with utils.redis_pipeline(transaction=True) as pipe: # optimization of redis request count cached_stolen = pipe.exists(stolen_key).execute() if not cached_stolen: cache_helper( pipeline=pipe, cache_key=stolen_key, cache_func=caching.cache_last_stolen, cache_args=(attacker, current_round, pipe), ) is_new, = pipe.sadd(stolen_key, flag.id).execute() return bool(is_new)
def cache_last_stolen(team_id: int, current_round: int, pipe: Pipeline) -> None: """ Caches stolen flags from "flag_lifetime" rounds. Just adds commands to pipeline stack, don't forget to execute afterwards. :param team_id: attacker team id :param current_round: current round :param pipe: redis connection to add command to """ game_config = game.get_current_game_config() with utils.db_cursor() as (_, curs): curs.execute( _SELECT_LAST_STOLEN_TEAM_FLAGS_QUERY, { 'round': current_round - game_config.flag_lifetime, 'attacker_id': team_id, }, ) flags = curs.fetchall() key = CacheKeys.team_stolen_flags(team_id) pipe.delete(key) if flags: pipe.sadd(key, *(flag[0] for flag in flags))