# Пошаговое руководство по курсу

> Различайте доказательство из лабораторной и данные развёртывания. Локальные упражнения ECVRF, VDF и лотерея выполняются без оплаты. В [витрине лотереи](https://lottery.modelmarket.dev/) сначала проверьте окружение и доказательства розыгрыша. UNI использует имитацию блокчейна. Транзакцию Base проверяйте в Base. Документированный вход в LIVE — бесплатное место за работу через `enterFromWork`, платные билеты выключены.

> **Для кого:** разработчики, которым нужна случайность, проверяемая самим игроком лотереи — до подключения `lottery/` на Base.
> **Языки:** `COURSE_LANG=en|ru|es` · строки UI в `i18n/` · этот гайд на русском.
> **English:** [step-by-step.md](./step-by-step.md) · **Español:** [step-by-step.es.md](./step-by-step.es.md)

---

## Зачем этот курс

Большинство лотерей и «случайных» розыгрышей доверяют оператору, который может тихо перекрутить невыгодный результат. Курс показывает криптографию, которая убирает это доверие: верифицируемый chaos-розыгрыш, функцию проверяемой задержки (VDF), доказывающую затраченную работу, доказательство ECVRF, проверяемое офлайн всего из **80 байт**, и time-lock RSW, скрывающий значение до тех пор, пока его не раскроет доказуемая работа. Затем вы **соберёте смещённую лотерею, сломаете её и почините** розыгрышем, который оператор не может перебирать — ровно тот паттерн, что потребляют контракты AIMarket `lottery/` на Base.

Каждая лаба обращается к **реальному оракулу AIMarket** — Platon, Chronos, Sortes, Aestus — запущенному локально, а не к моку. Каждая печатает блок `trace`, чтобы верифицируемый артефакт (доказательство, beta, puzzle, вектор VDF) можно было изучить, а не принимать на веру.

---

## Содержание

1. [Выбор трека](#выбор-трека)
2. [Установка (10 минут)](#установка-10-минут)
3. [M1 — Chaos VRF с Platon](#m1--chaos-vrf-с-platon)
4. [M2 — VDF-время с Chronos](#m2--vdf-время-с-chronos)
5. [M3 — ECVRF и Sortes](#m3--ecvrf-и-sortes)
6. [M4 — Time-lock RSW с Aestus](#m4--time-lock-rsw-с-aestus)
7. [M5 — Commit–reveal и атаки смещения](#m5--commitreveal-и-атаки-смещения)
8. [M6 — Проверка on-chain](#m6--проверка-on-chain)
9. [Чтение трейса (в каждой лабе)](#чтение-трейса-в-каждой-лабе)
10. [Упражнения и сертификат](#упражнения-и-сертификат)
11. [Мост: от курса к лотерее на Base](#мост-от-курса-к-лотерее-на-base)
12. [Чеклист самопроверки](#чеклист-самопроверки)
13. [Типичные проблемы](#типичные-проблемы)

---

## Выбор трека

| Трек | Модули | Время | Оракулы |
|------|--------|-------|---------|
| **Базовый** | M1 → M2 → M3 → M4 | ~2 ч | Platon, Chronos, Sortes, Aestus (локально) |
| **Экономика** | Базовый + M5 | +30 мин | Sortes (честная vs смещённая лотерея) |
| **Продвинутый** | Экономика + M6 | +30 мин | Chronos + Sortes → вектор для тестнета Base |

После **каждой** лабы смотрите блок `trace` — это её аудит-лог.

---

## Установка (10 минут)

### Шаг 1 — Клон и установка

Точные команды быстрого старта (обратите внимание на extra `[oracles,dev]` — `oracles` тянет Platon/Chronos/Sortes/Aestus, `dev` — pytest):

```bash
git clone https://github.com/alexar76/aimarket-courses.git
cd aimarket-courses/verifiable-randomness-course
pip install -e ".[oracles,dev]"
```

**Из монорепы aicom** (рекомендуется для контрибьюторов): пакеты оракулов подключаются автоматически по соседним путям в `oracles/` — `courselib/oracle_paths.py` поднимается до корня монорепы и добавляет `oracles/oracles/{chronos,sortes,aestus}` и `oracles/oracles/platon/backend` в `sys.path`. Кроме `numpy` для Platon, ничего ставить не нужно.

```bash
cd courses/verifiable-randomness-course
pip install -e ".[oracles,dev]"
```

### Шаг 2 — Тесты

```bash
pytest -q
```

**Ожидается:** все тесты зелёные (паритет i18n en/ru/es, хелперы случайности, проверки упражнений).

### Шаг 3 — Язык интерфейса лаб (опционально)

Строки UI (заголовки, метки `доказательство`/`проверка`/`трейс`, названия билетов) берутся из `i18n/{en,ru,es,fr,zh}.json`. Переключение через `COURSE_LANG`:

```bash
export COURSE_LANG=ru   # или es, по умолчанию en
python labs/lab01_platon_chaos.py
```

### Шаг 4 — Google Colab

1. Откройте [сайт курса](https://alexar76.github.io/aimarket-courses/verifiable-randomness-course/) → **Open in Colab** на любой лабе.
2. Выполните setup-ячейку (clone + `pip install -e ".[oracles,dev]"`).
3. При необходимости в setup-ячейке: `os.environ["COURSE_LANG"] = "ru"`.
4. Запустите ячейку лабы.

---

## M1 — Chaos VRF с Platon

**Концепция:** непредсказуемая случайность от верифицируемого chaos-оракула — chaos-state + client seed + энтропия ОС, связанные подписью Ed25519.
**Лаба:** `labs/lab01_platon_chaos.py` · **~15 мин** · нужен `numpy` + Platon

### Шаги

1. Прочитайте docstring в начале `labs/lab01_platon_chaos.py` (отрасль: лотереи, гейминг).
2. `python labs/lab01_platon_chaos.py`
3. В выводе: схема `доказательство`, первые байты розыгрыша, результат `проверка`.
4. `courselib/randomness.py` → `chaos_draw()`: временный `Signer`, chaos-`state`, `draw_randomness(...)`, затем `verify_randomness(...)`.

### Ожидаемый вывод

- `proof: scheme=platon-chaos-vrf/v1`, подписанный розыгрыш **32 байта**, `verify: True`
- блок `trace` с событием `chaos_draw` (`verified=True, bytes=32`)

### Самопроверка

- [ ] Объясняете, почему client seed + подпись делают розыгрыш непредсказуемым
- [ ] Видите 32 байта и `verify: True` против публичного ключа

---

## M2 — VDF-время с Chronos

**Концепция:** Wesolowski VDF доказывает затраченную *последовательную* работу в группе неизвестного порядка RSA-2048 — доказательство затраченной работы, а не доверие к системным часам.
**Лаба:** `labs/lab02_chronos_vdf.py` · **~20 мин** · нужен M1

### Шаги

1. `python labs/lab02_chronos_vdf.py`
2. Строка `доказательство`: proof `pi` и простое `l`; `verify: True`.
3. **Демо подделки:** лаба вызывает `vdf.verify(g, y+1, …)` на испорченном выходе — печатает `False`.
4. `courselib/randomness.py` → `vdf_eval()`: `hash_to_group` → `evaluate` → `prove` → `verify`. Используется `difficulty=700` (~700 возведений в квадрат) — считается за секунды.

### Ожидаемый вывод

- `proof: pi=…… l=<простое>`, `verify: True`, `проверка подделанного VDF: False`
- `trace` с событиями `vdf_eval` и `tamper_check`

### Самопроверка

- [ ] Формулируете, что VDF доказывает (последовательную работу) и что **не** доказывает (реальное время)
- [ ] Видели отказ проверки на подделанном выходе

---

## M3 — ECVRF и Sortes

**Концепция:** доказательство ECVRF по RFC 9381. Для фиксированной пары (публичный ключ, alpha) существует ровно **одна** валидная beta — оператор не может перебирать выгодный выход. Доказательство — **80 байт** и проверяется офлайн, без повторного обращения к оракулу.
**Лаба:** `labs/lab03_sortes_ecvrf.py` · **~20 мин** · нужен M1

### Шаги

1. `python labs/lab03_sortes_ecvrf.py`
2. Убедитесь, что доказательство ровно **80 байт** (`80 bytes = <hex>…`).
3. Отметьте `beta` (верифицируемый случайный выход) и `verify: True`.
4. `courselib/randomness.py` → `ecvrf_draw()`: `sk_to_pk` → `prove(sk, alpha)` → `verify(pk, alpha, pi)`.

### Ожидаемый вывод

- `proof: 80 bytes = <hex>…`, `result: beta=<hex>…`, `verify: True`
- `trace` с событием `ecvrf` (`proof_len=80, verified=True`)

### Самопроверка

- [ ] Объясняете «одна beta на (pk, alpha)» и почему это блокирует перебор
- [ ] Проверили, что доказательство **80 байт** (число офлайн-проверки из тэглайна)

---

## M4 — Time-lock RSW с Aestus

**Концепция:** головоломка time-lock RSW. Запечатайте значение сейчас; открыть его можно только после `T` последовательных возведений в квадрат — зашифруйте сейчас, расшифруйте после доказуемой задержки. Кирпич для запечатанных ставок и commit-reveal, где никто не должен подглядывать раньше времени.
**Лаба:** `labs/lab04_aestus_timelock.py` · **~20 мин** · нужен M2

### Шаги

1. `python labs/lab04_aestus_timelock.py`
2. Цикл seal → open: `T=350` возведений, затем открытый payload и `verify: True`.
3. `courselib/randomness.py` → `timelock_seal()`: `rsw.seal(message, T=...)` → `rsw.open_puzzle(puzzle)`; `message_match` подтверждает целостность открытого текста.

### Ожидаемый вывод

- `proof: T=350 squarings`, `result: <открытый payload>`, `verify: True`
- `trace` с событием `timelock` (`T=350, opened=True`)

### Самопроверка

- [ ] Объясняете, почему time-lock скрывает значение без доверенной третьей стороны
- [ ] Видели восстановление запечатанного сообщения после `T` возведений

---

## M5 — Commit–reveal и атаки смещения

**Концепция:** ключевой модуль. Соберите лотерею, которую нечестный оператор может перебирать, покажите смещение, затем замените розыгрыш на Sortes ECVRF, который оператор перебрать **не может**.
**Лаба:** `labs/lab05_commit_reveal.py` · **~25 мин** · трек: экономика · нужен M3

### Шаги

1. `python labs/lab05_commit_reveal.py`
2. Сравните билеты: `смещённый билет` vs `честный VRF-билет`. Строка `verify VRF: True` доказывает, что честный розыгрыш несёт верифицируемое доказательство.
3. Прочитайте **аудит-заметку**: «Честный розыгрыш привязывает одну beta к каждому seed — оператор не может перебирать.»
4. `courselib/randomness.py` → `BiasedLottery` (до 200 попыток перебора `operator_secret|player_seed|attempt` ради джекпота) vs `FairLottery` (один розыгрыш ECVRF → один билет, перебор невозможен).

### Ожидаемый вывод

- `смещённый билет: <n>` и `честный VRF-билет: <n>`, `verify VRF: True`, строка аудит-заметки
- `trace` с событиями `commit` и `draw` (`vrf_ok=True`)

### Самопроверка

- [ ] Описываете атаку перебором, которую делает `BiasedLottery`
- [ ] Объясняете, почему `FairLottery` (одна beta на seed) к ней невосприимчива

---

## M6 — Проверка on-chain

**Концепция:** упакуйте доказательства для on-chain-потребителя — вектор Wesolowski VDF под Solidity-верификатор плюс 32-байтовое слово лотереи из beta Sortes ECVRF.
**Лаба:** `labs/lab06_onchain_consumer.py` · **~20 мин** · трек: продвинутый · нужны M2, M3

### Шаги

1. `python labs/lab06_onchain_consumer.py`
2. Строка `протокол: wesolowski-vdf`, `verify: True` и 32-байтовое `слово лотереи: 0x…`.
3. `courselib/randomness.py` → `onchain_vdf_vector()` (отдаёт `scheme/seed/difficulty/y/pi/l/valid` строками под верификатор) и `lottery_word_from_vrf()` (первые 32 байта beta ECVRF).
4. **Мост:** лаба печатает deploy-заметку со ссылкой на [lottery/docs/deployments-base.md](https://github.com/alexar76/lottery/blob/main/docs/deployments-base.md) — живой верификатор `ChronosVDF.sol` на Base.

### Ожидаемый вывод

- `protocol: wesolowski-vdf`, `verify: True`, `слово лотереи: 0x<hex>…`, строка deploy-заметки
- `trace` с событиями `onchain_vdf` и `lottery_word`

### Самопроверка

- [ ] Называете два артефакта для on-chain-потребителя (вектор VDF + слово VRF)
- [ ] Знаете, где найти доки по деплою на Base

---

## Чтение трейса (в каждой лабе)

**Концепция:** каждая лаба пишет небольшой `Trace` (см. `courselib/trace.py`), чтобы поведение агента/оракула можно было изучить.

### Шаги (после каждой лабы)

1. Найдите блок `trace (<n> событий):` внизу вывода.
2. Прочитайте каждое событие — `kind` плюс залогированные поля (`chaos_draw` → `verified, bytes`; `vdf_eval` → `difficulty, valid`; `tamper_check` → `valid=False`).
3. Сопоставьте каждое событие со строкой stdout выше. Трейс — это «лог юнит-теста» верифицируемой случайности.

### Самопроверка

- [ ] Показываете событие, доказывающее, что розыгрыш прошёл проверку
- [ ] В M2 находите событие `tamper_check` со `valid=False`

---

## Упражнения и сертификат

После лаб запустите DIY-проверки. У каждого модуля своё упражнение в `courselib/exercises.py` (M2 подделывает VDF и проверяет, что verify падает; M3 проверяет ровно 80 байт; M5 — что честный розыгрыш проходит проверку):

```bash
python labs/run_exercises.py                       # все шесть, ✓/✗ по модулям
python labs/run_exercises.py --module m3           # проверка одного модуля
```

Когда все шесть зелёные — выпустите сертификат:

```bash
python labs/run_exercises.py --certificate "Иван Петров" --lang ru   # или --lang en / es
```

Создаётся `certificate.html` — откройте в браузере и **Print → Save as PDF**. ID удостоверения детерминированно выводится из имени + даты выпуска (`courselib/certificate.py`). Сертификат блокируется до прохождения упражнений, если не передать `--skip-check`.

---

## Мост: от курса к лотерее на Base

**Цель:** те же доказательства, что вы получили локально, проверяют контракты AIMarket `lottery/` на Base.

| Курс | Лотерея на Base |
|------|-----------------|
| M2 вектор Chronos VDF | верификатор Wesolowski `ChronosVDF.sol` |
| M3 beta Sortes ECVRF | 32-байтовое слово лотереи (M6 `lottery_word_from_vrf`) |
| M5 честный vs смещённый розыгрыш | почему потребитель берёт VRF, а не доверенный маяк |
| M6 `onchain_vdf_vector()` | тот самый вектор `{y, pi, l}` для верификатора |

Адреса деплоя и Solidity-верификатор — в [lottery/docs/deployments-base.md](https://github.com/alexar76/lottery/blob/main/docs/deployments-base.md).

---

## Чеклист самопроверки

Базовый трек:

- [ ] lab01–lab04 локально (и в Colab) на EN, RU, ES
- [ ] Объясняете chaos VRF, VDF, ECVRF и time-lock в одно предложение каждый
- [ ] Показываете отказ проверки на подделанном VDF (M2)
- [ ] Формулируете, почему 80-байтовое ECVRF проверяется офлайн (M3)

Экономика + продвинутый:

- [ ] lab05 + атака перебором и фикс через VRF
- [ ] lab06 + вектор VDF и слово лотереи под Base
- [ ] `pytest -q` зелёный и `python labs/run_exercises.py` — все ✓

---

## Типичные проблемы

| Проблема | Решение |
| --- | --- |
| `No module named 'chronos'` | `pip install -e ".[oracles]"` или запуск из монорепы aicom |
| `No module named 'oracle_core'` | `oracles/core` должен быть на пути — авто в монорепе; тянется `[oracles]` для Sortes/Platon |
| `Oracle package '…' not installed` | вне монорепы и без extra — поставьте `.[oracles]` или клонируйте рядом с `oracles/` |
| Сбой импорта Platon | поставьте `numpy`; проверьте `oracles/oracles/platon/backend` |
| Медленная VDF-лаба | лабы используют ~700 возведений (M2) / `T=350` (M4) — снижайте сложность только в своём упражнении |
| Пустые строки RU/ES | `export COURSE_LANG=ru\|es`; ключи `i18n/ru.json` должны совпадать с `i18n/en.json` |
| Ошибка i18n в `pytest` | добавьте недостающий ключ во **все три** каталога (en/ru/es) |
| Старый код в Colab | перезапустите setup-ячейку; зафиксируйте ветку `main` |

---

## Пересборка сайта и ноутбуков

После правок лаб или i18n:

```bash
python3 scripts/build_course_assets.py
```

Гайды в `docs/` правятся вручную — сначала EN, затем зеркала RU/ES.
