# Программа `shine_login_guard` Документ описывает целевое поведение программы классификации логинов SHiNE. Задача программы: - по логину определить класс регистрации; - вернуть результат в `return_data`; - не хранить PDA и не вести собственное состояние. ## 1. Назначение `shine_login_guard` — маленькая служебная программа, которую вызывает `shine_users` через CPI до регистрации пользователя. Она отвечает только на вопрос: - можно ли зарегистрировать логин автоматически; - является ли логин premium; - является ли логин trademark/company. ## 2. Program ID Текущий program id: - `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` ## 3. Состояние У программы нет PDA-состояний и persistent storage. Она опирается на статически сгенерированные словари, встроенные в бинарник на этапе сборки. ## 4. Внешние артефакты Словари хранятся в: - `src/dictionaries/premium/**/*.txt` - `src/dictionaries/trademarks/**/*.txt` На этапе build: - все `.txt` файлы рекурсивно собираются; - слова нормализуются; - дубль-слова детектятся и логируются как warning; - итоговые массивы слов вшиваются в generated Rust file. ## 5. Нормализация словаря Слово словаря считается валидным, если: - не пустое; - длина не больше `20`; - содержит только ASCII-буквы и цифры; - после `trim()` и `lowercase()` остаётся валидным. Символ `_` в словарных словах не допускается. ## 6. Единственная инструкция Инструкция: - `classify_login(login: String)` Бинарный ABI инструкции: ```text - tag: u8 = 1 - login_len: u32 LE - login_bytes[login_len]: UTF-8 ``` Аккаунты: - аккаунты не требуются Подпись транзакции для самой классификации не имеет бизнес-смысла. Если вызов идёт через CPI из `shine_users`, программа `shine_login_guard` не требует signer-аккаунтов вообще. ## 7. Выходные классы Программа возвращает `u32` через `set_return_data`: - `0` — `CLASS_FREE` - `1` — `CLASS_PREMIUM` - `2` — `CLASS_TRADEMARK` ## 8. Правила нормализации логина Перед классификацией логин нормализуется: - пустой логин запрещён; - длина исходного логина не больше `20`; - символ `_` удаляется; - остальные символы обязаны быть ASCII alnum; - буквы приводятся к нижнему регистру. Если нормализация не удалась: - логин считается `CLASS_PREMIUM`. Это намеренно жёсткое поведение: сомнительный логин не должен автоматически попадать в free-класс. ## 9. Правила классификации Алгоритм: 1. Нормализовать логин. 2. Попробовать разрезать его на `1..3` слов из словарей. 3. Если среди найденных частей есть trademark-слово, вернуть `CLASS_TRADEMARK`. 4. Если есть только premium-слова, вернуть `CLASS_PREMIUM`. 5. Если словарное совпадение не найдено и длина нормализованного логина `<= 7`, вернуть `CLASS_PREMIUM`. 6. Иначе вернуть `CLASS_FREE`. ## 10. Разбиение на слова Максимум слов в логине: - `3` Алгоритм разбиения: - DFS/backtracking по префиксам строки; - на каждом шаге берётся префикс длиной `1..20`; - если префикс найден в premium или trademark словаре, поиск продолжается на остатке строки; - если найден хотя бы один путь с trademark-словом, итог = `CLASS_TRADEMARK`; - если найден путь только с premium-словами, итог = `CLASS_PREMIUM`. ## 11. Инварианты Должно быть сохранено при переписи: - тот же способ нормализации логина; - та же семантика классов `0/1/2`; - тот же лимит `MAX_WORDS_PER_LOGIN = 3`; - тот же fallback `len <= 7 => premium`, если словарь ничего не нашёл; - тот же порядок приоритета `TRADEMARK > PREMIUM > FREE`. ## 12. Что не обязано сохраниться при переписи Можно менять без изменения бизнес-логики: - build pipeline словарей; - структуру хранения слов внутри бинарника; - способ поиска слов, если сохраняется тот же результат; - техническую форму возврата, если внешний контракт для `shine_users` останется совместимым. Но если меняется именно интерфейс `return_data` или классы, это уже требует обновления `shine_users` и документации.