Привяжите всё к одному блоку

Получите заголовок через `eth_getBlockByHash` и проверьте возвращённый хеш. Сохраните `chain ID`, номер блока и `stateRoot`. Вызовите `eth_getProof` с тем же хешем. Метка `latest` для воспроизводимой проверки не подходит: вершина цепи может измениться между запросами. Почему RPC-источники расходятся и когда блок считать устойчивым — отдельные вопросы.

Заголовок и доказательство от одного недоверенного узла могут быть согласованно ложными. Получите `stateRoot` через доверенную цепочку заголовков, лёгкий клиент или независимый источник. Здесь проверяется состояние относительно выбранного корня, а не консенсус Ethereum. `Execution APIs 1.0.0-beta.7` допускает номер, метку или хеш блока. В этой инструкции используется хеш. `EIP-1186` остаётся в статусе `Stagnant`, а не `Final`.

Что сохранить до проверки
ЗначениеТочный форматРоль
`chain ID`Целое значение сетиНе смешать одинаковый адрес в разных EVM-сетях
Хеш блока32 байтаСвязать заголовок и доказательство
`stateRoot`32 байта из заголовкаНачальный корень для `accountProof`
АдресРовно 20 байтПостроить путь аккаунта
Ключ хранилища`bytesMax32`, затем 32 байтаПостроить путь ячейки
Что сохранить до проверки
Значение
`chain ID`
Точный формат
Целое значение сети
Роль
Не смешать одинаковый адрес в разных EVM-сетях
Значение
Хеш блока
Точный формат
32 байта
Роль
Связать заголовок и доказательство
Значение
`stateRoot`
Точный формат
32 байта из заголовка
Роль
Начальный корень для `accountProof`
Значение
Адрес
Точный формат
Ровно 20 байт
Роль
Построить путь аккаунта
Значение
Ключ хранилища
Точный формат
`bytesMax32`, затем 32 байта
Роль
Построить путь ячейки

Пройдите `accountProof` от `stateRoot` до аккаунта

`accountProof` — массив узлов дерева Меркла — Патриции (`Merkle Patricia Trie`). Узлы записаны в двоичной кодировке `RLP`. Путь начинается с доверенного `stateRoot`. Его ключ — результат хеш-функции `Keccak-256` от 20 байт адреса. Хеш делится на полубайты, по которым проверяющий код проходит дерево.

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

  1. 1
    Декодируйте входы

    Адрес должен дать ровно 20 байт, корень и хеши — по 32. Не хешируйте строку `0x…` как текст UTF-8.

  2. 2
    Проверьте связи узлов

    Начните с `stateRoot`. Для каждого дочернего хеша найдите соответствующий узел, декодируйте `RLP` и продолжите по нужным полубайтам.

  3. 3
    Декодируйте найденную запись

    Лист аккаунта содержит `RLP`-список `[nonce, balance, storageRoot, codeHash]`. Сравните все четыре поля с ответом, включая поле ответа `storageHash`.

Путь может корректно закончиться отсутствующим дочерним узлом или несовпадающим сжатым путём. Доказательство хранит самый длинный существующий префикс. В клиенте `go-ethereum` функция `VerifyProof` тогда возвращает `nil` без ошибки. Это доказанное отсутствие аккаунта относительно `stateRoot`, а не придуманный нулевой лист.

От доказанного `storageRoot` перейдите к ячейке

Ячейка хранилища (`storage slot`) — 32-байтовая позиция в состоянии контракта. Поле `storageKeys` принимает `bytesMax32`. Декодируйте его как беззнаковое число от старшего байта к младшему (`big-endian`). Значение длиннее 32 байт отклоните, короткое дополните нулями слева. Затем вычислите `Keccak-256` от всех 32 байт.

Порядок проверки каждой записи `storageProof`

  • для простой переменной возьмите номер ячейки; для отображения (`mapping`) и динамического массива сначала вычислите позицию по разметке контракта;
  • начните путь от `storageRoot`, полученного из проверенного листа аккаунта, а не от `stateRoot` блока;
  • сверьте поле `key` с нормализованным ключом и проверьте все `RLP`-узлы по его хешу;
  • декодируйте найденный лист как `RLP`-целое без ведущих нулей и сравните с полем `value`;
  • если корректный путь доказывает отсутствие листа, значение этой ячейки равно нулю в выбранном блоке.

Ключ ячейки — не calldata и не порядковый номер поля из обозревателя. Если calldata и разметка хранилища смешаны, доказательство будет проверяться по другой позиции.

Сверьте результат и сохраните причину отказа

Несовпадение и ближайшая проверка
Что не сошлосьЧастая причинаСледующий ход
Первый узел не соответствует `stateRoot`Заголовок и доказательство взяты из разных блоковПовторить запрос по точному хешу
Путь аккаунта расходитсяХешировали текст или расширили адрес до 32 байтХешировать ровно 20 декодированных байт
Поля аккаунта отличаютсяОтвет не соответствует доказанному листуОтклонить ответ и сохранить узлы
Путь ячейки расходитсяВыбран неверный корень или рассчитана другая позицияНачать от доказанного `storageRoot` и пересчитать ключ
Получился нольСмешаны нулевой лист и отсутствие листаСохранить конечный тип пути и результат проверяющего кода
Несовпадение и ближайшая проверка
Что не сошлось
Первый узел не соответствует `stateRoot`
Частая причина
Заголовок и доказательство взяты из разных блоков
Следующий ход
Повторить запрос по точному хешу
Что не сошлось
Путь аккаунта расходится
Частая причина
Хешировали текст или расширили адрес до 32 байт
Следующий ход
Хешировать ровно 20 декодированных байт
Что не сошлось
Поля аккаунта отличаются
Частая причина
Ответ не соответствует доказанному листу
Следующий ход
Отклонить ответ и сохранить узлы
Что не сошлось
Путь ячейки расходится
Частая причина
Выбран неверный корень или рассчитана другая позиция
Следующий ход
Начать от доказанного `storageRoot` и пересчитать ключ
Что не сошлось
Получился ноль
Частая причина
Смешаны нулевой лист и отсутствие листа
Следующий ход
Сохранить конечный тип пути и результат проверяющего кода

Сохраните `chain ID`, заголовок, запрос, ответ и версии проверяющих библиотек. Повторите вычисление другой реализацией, не спрашивая правильный ответ у того же поставщика RPC. Если ячейка хранилища относится к прокси-контракту, сначала докажите разметку и точную позицию. Состояния прокси-контракта и его реализации различаются. `eth_getProof` даёт обычное доказательство дерева Меркла — Патриции. А zero-knowledge proof — доказательство с нулевым разглашением — решает другую задачу.

Ответ нужно отклонить, если

  • нет точного хеша блока или доверенного `stateRoot`;
  • заголовок и `eth_getProof` относятся к разным блокам либо сетям;
  • `accountProof` начинают от `storageRoot`, а `storageProof` — от `stateRoot`;
  • пустой массив узлов принимают за доказательство отсутствия без проверки пути;
  • архивный RPC считают доказательством сам по себе, не пересчитывая узлы дерева.

Источники

Мы используем прямые ссылки и фиксируем дату проверки. Полный текст чужих материалов не перепечатывается.

  1. Ethereum Execution APIs 1.0.0-beta.7 — eth_getProof Ethereum Execution APIs · Проверено 25 августа 2026 г. в 00:08 GMT+5
  2. Execution APIs v1.0.0-beta.7 pinned state method schema Ethereum Execution APIs · Проверено 25 августа 2026 г. в 00:08 GMT+5
  3. EIP-1186 — RPC method to get Merkle proofs Ethereum Improvement Proposals · Проверено 25 августа 2026 г. в 00:08 GMT+5
  4. Ethereum Yellow Paper Ethereum · Проверено 25 августа 2026 г. в 00:08 GMT+5
  5. go-ethereum trie proof implementation go-ethereum · Проверено 25 августа 2026 г. в 00:08 GMT+5
Материал носит информационный характер и не является индивидуальной рекомендацией. Криптоактивы связаны с риском волатильности, технических сбоев, хищения и полной потери средств.