«Сломай и почини»
оценку ставят на защите
Три части. Сдаёшь через платформу на странице /hw/1. Код и отчёт — это билет на защиту, а не сама оценка: оценку ставят по защите (см. в конце). На защите ты один.
Инструмент — Python SDK defilab. Ставится одной строкой, нужен Python 3.11 или новее:
pip install "defilab[plot] @ https://defilab.insights-forge.com/sdk/defilab-0.1.5-py3-none-any.whl"[plot] — это matplotlib, он нужен для графика в части 1. Без него всё остальное работает, а график просто не нарисуется.
Код класса, имя и PIN — те же, что для входа на сайт; код класса называет преподаватель.
from defilab import Lab, sim
lab = Lab("https://api.defilab.insights-forge.com", class_code="DEFI-01", name="Твоё Имя", pin="твой PIN")
lab.wallet() # проверка входа: адрес, баланс, сколько транзакций ждёт блокаТебе выдаётся игрушечный чейн с настраиваемыми параметрами. Свои параметры возьми с сервера — у каждого они свои:
p = lab.hw.params()
# block_time_s — среднее время блока; latency_ms — задержка сети;
# give_up_deficit — на сколько блоков может отстать атакующий, прежде чем сдаётся;
# shares — доли хэшрейта для ответа; target_probability — какую вероятность торговец считает приемлемойЧто сделать:
- Провести атаку в симуляторе и найти условия, при которых double spend удаётся:
sim.attack(hashrate_share=0.3, confirmations=1, params=p, trials=5000)- Ответить на вопрос: сколько подтверждений нужно ждать при доле атакующего 10%, 30%, 45%, чтобы вероятность успешной атаки была не выше
target_probability. Используйsharesиз своих параметров (там же есть четвёртая, твоя личная доля). - Построить график «подтверждения × доля хэшрейта» (по оси X — число подтверждений, по оси Y — вероятность, лог-шкала; по одной линии на каждую долю):
table = sim.sweep(p["shares"], range(0, 13), params=p, trials=2000)
sim.plot(table, "attack.png")LLM может написать симулятор или график. Но параметры ты подбираешь сам, график объясняешь сам и защищаешь на защите. Приложи график и ответы (число подтверждений для каждой доли) в отчёт, а сами числа — в поле parts.part1.
Полезно сравнить свою кривую с формулой из статьи Накамото (sim.nakamoto(q, z)) и объяснить, почему они совпадают или расходятся (задержка сети, конечные ресурсы атакующего).
Написать и задеплоить в класс-чейн один контракт из списка:
- эскроу с арбитром — деньги держит контракт, арбитр решает спор;
- аукцион — ставки, победитель, возврат проигравшим;
- лотерея — участники, розыгрыш, выплата;
- копилка с дедлайном — вносишь, снимаешь только после срока.
Условие: контракт должен содержать одну уязвимость, которую ты знаешь и описал заранее. Описание кладёшь «в конверт»: на сервер уходит только его sha256, сам текст остаётся у тебя до конца окна.
src = open("MyContract.sol").read()
d = lab.deploy(src, args=[...]) # компиляция на сервере: один файл, импорты только @openzeppelin
lab.wait(d)
lab.hw.arena_register(d.address, envelope="здесь я описываю свою дыру словами",
kind="piggybank", source=src, abi=d.abi)Адреса для конструктора (продавец, арбитр, кто угодно) — это адреса кошельков класс-чейна: свой даёт lab.wallet()["address"], чужой попроси у одноклассника. Для проверки можно поставить свой адрес во все роли. Деплой и вызовы стоят комиссию в CLASS; если lab.deploy отвечает insufficient CLASS for value + fee, монет на кошельке нет — попроси преподавателя пополнить.
У арены нет страницы и кнопки: это доска в SDK, lab.hw.arena(). Общий зал в кабинете — другое место, там майнинг стадий 4–6, к домашке он не относится. Другие ученики (и агенты-атакующие) читают исходники с этой доски и пытаются найти дыры. Твоя задача — ещё и найти чужие: провести эксплойт-транзакцию в класс-чейне и засчитать её.
board = lab.hw.arena() # чужие контракты, исходники, лидерборд
entry = board["entries"][0] # берёшь любую чужую запись
target, target_abi = entry["contract"], entry["abi"] # адрес и ABI лежат прямо в ней
print(entry["source"]) # исходник — читать его и есть задача
tx = lab.call(target, "нужная_функция", [...], abi=target_abi)
lab.wait(tx)
lab.hw.arena_claim(target, tx.hash) # засчитывается, если tx реально увела CLASS или сработала на целиОчки: за то, что твою уязвимость не нашли к концу окна, и за каждую найденную чужую (первому, кто сломал контракт, — бонус). Правила и подсчёт — в hw1-attack-arena.md. Это учит читать чужой код — в эпоху LLM это важнее, чем писать.
После окна открой конверт (lab.hw.arena_open_envelope(текст)) — платформа сверит его с зарегистрированным sha256.
У тебя есть токены A, у друга — B. Опиши механизм обмена, при котором никто не может кинуть.
- Сначала — для одной конкретной сделки между вами двумя (атомарный своп: двумя контрактами или одним, который держит обе ноги). Опиши по шагам, что происходит и почему нельзя забрать чужое, не отдав своё.
- Потом — сложнее: а если ты хочешь обменять A на B в любой момент, ни с кем конкретным не договариваясь? Ответа тут не требуется. Нужна попытка и список проблем, в которые ты упёрся (кто назначает цену? откуда берётся вторая сторона? что если её нет?). С разбора этих попыток начнётся лекция 2.
Текст части 3 — в поле parts.part3 или в отчёте.
lab.hw.submit(1,
parts={
"part1": {"confirmations": {"0.1": 4, "0.3": 24, "0.45": None}, "notes": "..."},
"part2": {"contract": d.address, "kind": "piggybank"},
"part3": {"text": "..."},
},
report_md=open("report.md").read(),
files=[{"path": "MyContract.sol", "content": src}, {"path": "attack.py", "content": "..."}],
)Отчёт (report.md) — свободный текст: что ты сделал в каждой части, график части 1 с объяснением, какой контракт и почему, попытки части 3.
Использовать LLM разрешено и поощряется — мы учим работать с инструментом, а не запрещаем его. Но:
- В отчёте обязателен раздел «Что делал LLM, что делал я, где LLM ошибся». Без него работа считается несданной.
- На защите ты один, без LLM. Защита проверяет, что ты понимаешь свой код и можешь его быстро менять.
Защита письменная, в чате на /hw/1, 15–20 минут. Её ведёт агент-экзаменатор. Он:
- задаёт вопросы по твоему коду с цитированием строк («на строке N ты берёшь X — почему?»);
- просит изменить поведение прямо сейчас, под таймером (например: «сделай, чтобы снять могли только двое, а не кто угодно — покажи, где правишь»). Новый код ты присылаешь через SDK, агент его компилирует и проверяет. Это главный фильтр: кто не понимает свой код, не может его быстро править, а копировать в LLM под таймером — медленно и заметно;
- задаёт контрфактуальные вопросы («что было бы, если бы дедлайн был в прошлом?»);
- проверяет связи с лекцией (двойная трата, подтверждения, атомарность, залог).
После защиты агент выставляет предварительную оценку, флаги и сводку. Итоговую оценку подтверждает преподаватель. Если не согласен — апелляция в один клик (lab.hw.defense_appeal("...")), решает преподаватель. Что именно проверяет экзаменатор и по какой шкале — в rubric.md.
- Понимает, что такое double spend и почему подтверждения снижают его вероятность.
- Может объяснить свой график: почему кривая падает с числом подтверждений и растёт с долей хэшрейта; почему при доле ≥ 50% никакое число подтверждений не спасает.
- Знает, откуда взял числа «сколько подтверждений при 10/30/45%», и что меняется от задержки сети и от того, что у атакующего конечные ресурсы (
give_up). - Контрфактуал: «а если бы блок был вдвое быстрее / задержка вдвое больше — что с ответом?»
- Главный критерий. Здесь ловится вайбкодинг.
- Объясняет, что делает каждая существенная функция его контракта, построчно по запросу.
- Знает свою заложенную уязвимость и почему она уязвимость (какой инвариант нарушается, кто и как выигрывает). Может отличить её от случайных багов.
- Живая правка под таймером (2–4 минуты): «измени поведение вот так прямо сейчас, покажи, где». Примеры: закрыть заложенную дыру; добавить проверку
msg.sender; поменять условие дедлайна; запретить повторный вызов. Ученик присылает новый код через SDK, экзаменатор компилирует его (инструментcompile) и проверяет, что правка действительно меняет поведение и компилируется. нерелевантный сгенерированный кусок → не понимает. - Читает чужой код: может рассказать, как нашёл (или искал) чужую уязвимость на арене.
- Объясняет, почему наивный обмен «в два перевода» позволяет кинуть (первый отдал — второй не прислал), и как атомарность это чинит.
- Может описать свой механизм свопа по шагам и назвать, что его держит честным.
- Для открытого вопроса (обмен «в любой момент») — называет реальные проблемы: цена, поиск контрагента, ликвидность. Не требуется решение, требуется осмысленная попытка.
- Связывает домашку с лекцией: подтверждения ↔ раунд майнеров, атомарность ↔ раунд контрактов, залог/slash ↔ раунд The Merge.
- Раздел отчёта про LLM заполнен честно: видно границу «что делал я / что делал LLM / где LLM ошибся», и на защите это подтверждается (ученик знает, где инструмент ошибся, и почему).
Страница /hw/1 → «Арена». Здесь ученики деплоят контракты с заложенной уязвимостью и ищут дыры друг у друга. Смысл — научиться читать чужой код: в эпоху LLM это важнее, чем писать.
Как участвовать
- Задеплой свой контракт в класс-чейн через SDK (
lab.deploy). Он должен содержать одну уязвимость, которую ты знаешь. - Опиши уязвимость словами в «конверте». На сервер уходит только
sha256(текста)— сам текст остаётся у тебя. Так ты фиксируешь, что знал дыру заранее, но не подсказываешь её другим. - Зарегистрируй контракт:
lab.hw.arena_register(address, envelope="...", kind=..., source=src, abi=...). Исходник виден всем участникам — это то, что они читают. - Ищи чужие дыры. Провёл эксплойт-транзакцию — засчитай её:
lab.hw.arena_claim(target, tx.hash).
Один контракт на ученика (перерегистрация обновляет запись). Контракт можно зарегистрировать только свой — тот, что задеплоен с твоего кошелька (платформа проверяет по индексеру).
Окно
Окно арены — 5 дней с момента, как в классе зарегистрирован первый контракт. Пока окно открыто:
- чужие конверты закрыты (виден только
sha256и исходник); - взломы засчитываются и появляются в ленте.
После закрытия окна каждый открывает свой конверт (lab.hw.arena_open_envelope(текст)); платформа сверяет его с зарегистрированным хэшем. Тогда же начисляются очки за «не нашли».
Как засчитывается «взлом»
Взлом — это не заявка словами, а эксплойт-транзакция в класс-чейне, засчитанная индексером по эффекту на контракт-цель. arena_claim(target, tx_hash) принимается, если:
- транзакция отправлена с твоего кошелька (через chain-service);
- она замайнена и не завершилась revert;
- она либо увела CLASS из контракта-цели (баланс цели в блоке транзакции меньше, чем в предыдущем), либо вызвала цель и породила её событие (взаимодействие с контрактом, изменившее его состояние).
Нельзя засчитать взлом собственного контракта. Один зачёт на пару (атакующий → цель): повторная claim обновляет транзакцию-доказательство, но не даёт вторых очков.
Спорные случаи (например, «сработало, но CLASS не двигался») индексер помечает, разбирает преподаватель.
Очки
- +30 — твою уязвимость не нашли к концу окна (никто не засчитал взлом твоего контракта).
- +20 — за каждый засчитанный взлом чужого контракта.
- +10 — бонус тому, кто сломал контракт первым (в дополнение к +20).
Свой контракт сломать нельзя, очков за это нет. Если твой контракт сломали — за «не нашли» ты не получаешь ничего, но очки за чужие взломы остаются.
Пример: ты сломал два чужих контракта (один — первым), а твой никто не тронул до конца окна: 30 (не нашли) + 20 + (20 + 10) = 80.
Лидерборд арены публичный внутри класса (lab.hw.arena()["leaderboard"]), обновляется в реальном времени. Он же — вход для экзаменатора: на защите тебя спросят, как ты искал чужие дыры и почему твоя (не) устояла.
Сдача и защита — из кода, кнопок на этой странице нет. Порядок такой:
pip install https://defilab.insights-forge.com/sdk/defilab-0.1.5-py3-none-any.whl
from defilab import Lab
lab = Lab("https://api.defilab.insights-forge.com",
class_code="DEFI-01", name="Твоё Имя", pin="твой PIN")
# сдать работу (можно пересдавать, пока не начал защиту)
lab.hw.submit(1, parts={...}, report_md=open("report.md").read(),
files=[{"path": "MyContract.sol", "content": src}])
# защита: начинается один раз, после старта пересдача закрывается
lab.hw.defense_start()
lab.hw.defense() # прочитать вопрос экзаменатора
lab.hw.defense_say("ответ", code=None)
lab.hw.defense_stop() # закончить и получить предварительную оценкуЧто писать в parts — в разделе «Формат сдачи» выше. Пока защита не начата, каждый новый submit заменяет предыдущий.