Перейти к содержанию

Ethereum и EVM: детерминированное выполнение контракта

Обычный сервер может получить текущую цену, открыть файл и обратиться к базе данных. Контракт Ethereum тоже обрабатывает запросы, но условия исполнения у него другие. Чтобы понять эти условия, рассмотрим реестр учебной библиотеки. Издатель объявляет выпуск курса 17, а читатель проверяет, какой выпуск сейчас считается действующим.

Контракту не требуется хранить весь учебник. Достаточно номера курса, отпечатка содержимого и признака активности. Это небольшая модель, на которой удобно различить вычисление, постоянное состояние и данные, поступающие снаружи. Начнём с этих границ, прежде чем выбирать синтаксис Solidity.

Один вход и один результат

EVM — виртуальная машина, исполняющая код контракта. Важное свойство такой машины заключается в воспроизводимости: одинаковое исходное состояние и одинаковые входные данные должны приводить к одинаковому результату. Узлы не договариваются, какой ответ прислал любимый HTTP-сервис одного разработчика. Они проверяют переход общего состояния по правилам сети.

Представим запись 17 → H1, true. Обозначение H1 означает конкретные 32 байта, а не имя файла. Издатель просит заменить их на H2. Для вычисления результата нужны код функции, прежняя запись и параметры вызова. Дата изменения локального файла разработчика и содержимое вкладки браузера в этот набор не входят. Следовательно, контракт не узнает о новом уроке только потому, что редактор сохранил Markdown.

Введём модель перехода, пока без программы:

До:   course[17] = {hash: H1, active: true}
Вход: publisher вызывает publish(17, H2)
После: course[17] = {hash: H2, active: true}
Наблюдение: событие Published(17, H2)

Эта запись помогает увидеть, что функция не производит текст курса. Она меняет указатель на признанный издателем выпуск. Если файл с отпечатком H2 исчезнет со всех доступных хранилищ, контракт продолжит хранить тот же отпечаток. Воспроизводимость состояния и доступность содержимого — разные результаты, за которые отвечают разные части приложения.

Входом для EVM служит не название кнопки «Обновить», а закодированный вызов. Сайт помогает человеку выбрать действие и подготовить параметры. Убрав кнопку, мы изменим интерфейс, но не права контракта. Любой участник, знающий интерфейс функции, может попытаться вызвать её другим клиентом. Поэтому ограничения проверяются там, где хранится состояние.

Кто предлагает изменение

Транзакция доставляет намерение отправителя в сеть. Это предложение выполнить действие, а не доказательство, что действие уже произошло. Между подготовкой, подписью, передачей узлу, включением и окончательной устойчивостью блока есть отдельные состояния. В нашем реестре важно отличать «редактор отправил новый отпечаток» от «читатель прочитал новый отпечаток из подтверждённого состояния».

Пусть издатель отправил две публикации подряд: сначала H2, затем H3. Текущая запись после их успешного исполнения содержит H3, а история может содержать оба события. Если первая публикация откатилась, история успешных изменений отличается. Обычный список отправленных хешей транзакций не заменяет журнал принятых переходов.

Ethereum использует proof of stake. Для понимания курса не требуется превращать это в урок о валидаторской инфраструктуре. Достаточно сохранить различие между правилами исполнения EVM и правилами выбора согласованной истории. Контракт вычисляет свой переход, а сеть определяет, в какой последовательности такие переходы вошли в общее состояние. Старое описание Ethereum как действующей сети майнеров нельзя незаметно переносить в новый текст.

Также не следует считать EVM обычной распределённой файловой системой. Узлы действительно хранят необходимые данные сети, но публикация большого массива байтов требует ресурсов. Если задача решается хранением короткого отпечатка, загрузка всех Markdown-файлов внутрь контракта создаст другой проект с другой стоимостью и ограничениями.

Четыре части учебной библиотеки

Разделим приложение на части по ответственности. Редакционный инструмент подготавливает выпуск и вычисляет отпечаток. Хранилище предоставляет файл. Контракт сообщает, какой отпечаток назначен издателем. Браузер сопоставляет скачанные байты с этой записью и объясняет результат пользователю. Ни одна из частей не должна незаметно выдавать свой результат за результат остальных.

Часть Достоверно сообщает Не доказывает сама по себе
Реестр Текущий отпечаток и активность Качество и авторство текста
Хранилище Полученные байты файла Полномочия его издателя
Сайт Результат выбранных запросов Независимость от собственного сервера
Подпись отправителя Разрешение определённого адреса Личность человека без внешней связи

Рассмотрим ошибочный проект: контракт содержит только URL https://example.test/course17.md, а сайт утверждает, что текст неизменен навсегда. По тому же URL завтра можно отдать другой файл. Постоянство строки адреса не означает постоянство ответа. Добавление отпечатка меняет проверяемое свойство: теперь полученные байты можно сравнить с ожидаемым выпуском. Однако даже это не гарантирует, что файл обязательно удастся скачать.

Есть и обратная ошибка. Разработчик помещает весь текст в публичное состояние и рассчитывает скрыть его, объявив поле private. В Solidity это ограничение доступа из исходного кода других контрактов, а не шифрование. Наблюдатель может исследовать публичные данные сети. Для учебной библиотеки поэтому выбираем сведения о выпуске, а не почту читателя или личную историю покупок.

В результате мы получили не контракт, а точное распределение обязанностей будущего приложения. Дальнейшие уроки введут адреса, параметры сборки и состояние, но каждый новый механизм должен оставаться внутри этой модели: реестр хранит объявление издателя, внешние системы доставляют и показывают материал. Модель Ethereum помогает сверить границу исполнения с устройством сети.

Где заканчивается согласие узлов

Представим, что все узлы согласованно сохранили ошибочный отпечаток, который передал издатель. Машина выполнила разрешённое действие одинаково, но редакционная ошибка осталась. Это не противоречие: согласие относится к правилам перехода и входу, а не к полезности опубликованного урока. Поэтому перед подписью нужна обычная проверка подготовленного артефакта человеком и инструментом редакции.

Есть ещё независимый источник ошибки — выбранный RPC. Браузер может получить устаревший ответ или обратиться к другой сети. Детальная подпись «прочитано из реестра C на блоке B» позволяет обсуждать такое расхождение. Фраза «блокчейн сказал» скрывает сведения, без которых нельзя восстановить наблюдение.

Для самостоятельного разбора возьмите изменение заголовка файла без публикации нового hash. Контракт продолжает сообщать прежнее значение, сайт отдаёт новые байты, сравнение не проходит. Назовите, какая часть процесса не выполнена. Ответ связан с объявлением выпуска, а не с необходимостью перенести HTTP внутрь EVM.

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии