| Слой | Обязательные поля | Что он доказывает |
|---|---|---|
| Source | tx hash, canonical block, emitterChain, emitterAddress, sequence | Какое on-chain сообщение наблюдали Guardians |
| VAA | body bytes, digest, guardianSetIndex, signatures | Что supermajority подписало именно это тело |
| Destination | chain, receiving app, VAA/message hash, result event или state | Что нужное приложение обработало сообщение |
- Слой
- Source
- Обязательные поля
- tx hash, canonical block, emitterChain, emitterAddress, sequence
- Что он доказывает
- Какое on-chain сообщение наблюдали Guardians
- Слой
- VAA
- Обязательные поля
- body bytes, digest, guardianSetIndex, signatures
- Что он доказывает
- Что supermajority подписало именно это тело
- Слой
- Destination
- Обязательные поля
- chain, receiving app, VAA/message hash, result event или state
- Что он доказывает
- Что нужное приложение обработало сообщение
Тройка emitter идентифицирует сообщение только после проверки source history
VAA индексируется по emitter_chain, 32-байтовому Wormhole-formatted emitter_address и sequence этого emitter. Sequence — счётчик сообщений конкретного emitter, а не nonce пользовательского кошелька и не номер source-транзакции. Из source tx сохраните событие Core Contract и декодируйте эти три поля; адрес приложения из интерфейса не заменяет emitter_address в самом событии.
Одинаковая на вид тройка безопасно связывает историю только при каноничном source block. Документация Wormhole отдельно предупреждает о reorg при низком consistency level: разные ветви цепочки могут породить разные тела. Проверьте source finality и реорганизации по правилам выбранной сети; только затем фиксируйте tuple и payload.
Header выбирает guardian set, а подписи относятся к хешу body
Header содержит version, guardian_set_index и массив signatures. Body содержит timestamp, nonce, emitter_chain, emitter_address, sequence, consistency_level и payload. Подписываемый digest получают двойным keccak256 от точных bytes body. Перекодирование payload, адреса или чисел создаёт другой digest, даже если текстовая карточка выглядит одинаково.
Проверка VAA без доверия к одной карточке
- 1Получить исходные bytes
Найдите VAA через официальный SDK или Wormholescan по source tx либо emitter tuple. Сохраните serialized bytes и сеть, а не только статус.
- 2Разобрать body
Сверьте emitterChain, emitterAddress, sequence, consistencyLevel и application payload с source event. Несовпадение любого поля останавливает проверку.
- 3Посчитать digest
Используйте реализацию Wormhole SDK или Core Contract: спецификация подписывает double-keccak body. Не заменяйте её обычным хешем всей VAA.
- 4Выбрать guardian set
Получите set по guardianSetIndex, проверьте уникальные индексы signer и достаточный supermajority, а на destination Core — что этот set ещё принимается. Текущее описание сети приводит 13 из 19, но код не должен зашивать это число навсегда.
- 5Проверить подписи
Проверяйте ECDSA над точным digest через официальный Core/SDK path. Количество байтов в массиве ещё не доказывает валидный quorum.
Если источник и VAA расходятся, сначала исключите неверную сеть, Wormhole-formatted address и другую ветвь цепочки. Устройство RPC объясняет, почему один сервер может ещё не видеть каноничный source block; другой API-ответ не разрешает игнорировать несовпавшее тело.
Redemption ищут в receiving application, а не в header VAA
Базовый VAA не содержит универсального destination и фактически multicast: любой Core Contract сети Wormhole может проверить его подлинность. Назначение и действие определяет application payload. Поэтому сначала декодируйте формат конкретного протокола, установите destination chain и receiving contract, а затем ищите его on-chain result.
| Наблюдение | Доказанный этап | Чего ещё не хватает |
|---|---|---|
| Source event есть, VAA нет | Сообщение опубликовано | Finality, guardian observation или quorum |
| VAA signatures валидны | Guardians аттестовали body | Обработка receiving application |
| Explorer показывает destination tx | Есть кандидат на доставку | Validated receipt и app-specific result |
| Receiving contract пометил message consumed | Это приложение обработало VAA | Проверка ожидаемого state change |
| Повторный submit отклонён как consumed | Replay protection сработала | Это не доказывает, что первый вызов дал нужный бизнес-результат |
- Наблюдение
- Source event есть, VAA нет
- Доказанный этап
- Сообщение опубликовано
- Чего ещё не хватает
- Finality, guardian observation или quorum
- Наблюдение
- VAA signatures валидны
- Доказанный этап
- Guardians аттестовали body
- Чего ещё не хватает
- Обработка receiving application
- Наблюдение
- Explorer показывает destination tx
- Доказанный этап
- Есть кандидат на доставку
- Чего ещё не хватает
- Validated receipt и app-specific result
- Наблюдение
- Receiving contract пометил message consumed
- Доказанный этап
- Это приложение обработало VAA
- Чего ещё не хватает
- Проверка ожидаемого state change
- Наблюдение
- Повторный submit отклонён как consumed
- Доказанный этап
- Replay protection сработала
- Чего ещё не хватает
- Это не доказывает, что первый вызов дал нужный бизнес-результат
Для token transfer проверяют контракт соответствующего продукта, его transfer-completed state или событие и изменения токена. Для произвольного message это может быть другой receiver и другая replay-защита. Общий материал про мосты помогает назвать этапы, но не подменяет emitter tuple, VAA digest и app-specific consumption.
На EVM destination transaction может успешно попасть в блок, а inner call receiving contract — завершиться иначе; receipt, logs и trace отвечают на разные части вопроса. Проверка внутренних вызовов нужна, когда внешний status не объясняет, применил ли receiver payload.
Повторная доставка допустима только после доказательства состояния consumption
- не отправляйте VAA в адрес контракта, взятый из поисковой рекламы или чата;
- не заменяйте emitter_address адресом пользовательского кошелька;
- не считайте len_signatures доказательством валидных signer и quorum;
- не отправляйте VAA, если destination Core уже отклоняет его guardian set как expired;
- не называйте source tx финальным, не проверив consistency level и canonical block;
- не повторяйте redeem, пока не проверены destination receipt и consumption state;
- не передавайте seed-фразу, private key или подпись кошелька для чтения публичного VAA.
Если VAA валиден, но destination state отсутствует, сохраните bytes, tuple, digest, guardianSetIndex, source finality и все destination tx hashes. После этого повторяют только официальный app-specific delivery path. Если message уже consumed, новый submit не является восстановлением; разбирать нужно результат первого вызова.
Если target Core отвергает исторический guardian set как expired, подписи старых ключей уже недостаточны для redemption. Не переписывайте header вручную: официальный recovery-flow получает observations текущего set, заменяет устаревшие подписи, повторно проверяет тот же body и требует завершить Guardian approval. Локально пересобранные bytes нельзя считать одобренным VAA до этого финального шага.
Источники
Мы используем прямые ссылки и фиксируем дату проверки. Полный текст чужих материалов не перепечатывается.
- Wormhole Docs — Verified Action Approvals Wormhole Foundation · Проверено 30 августа 2026 г. в 15:40 GMT+5
- Wormhole Docs — Security Wormhole Foundation · Проверено 30 августа 2026 г. в 15:40 GMT+5
- Wormhole Docs — Fetch a Signed VAA Wormhole Foundation · Проверено 30 августа 2026 г. в 15:40 GMT+5
- Wormhole Docs — Consistency Levels Wormhole Foundation · Проверено 30 августа 2026 г. в 15:40 GMT+5
- Wormhole Docs — Replace Outdated Signatures in VAAs Wormhole Foundation · Проверено 30 августа 2026 г. в 15:40 GMT+5
