НОВОЕ: Helius приобретает Light Protocol
Как работать с ошибками блокхеша в Solana
Блог/Разработка

Как работать с ошибками блокхеша в Solana

Инженер по удобству разработкиOwen Venter в XOwen Venter в LinkedIn
Чтение: 7 мин

Что такое блокхеш?

Чтобы понять, что такое блокхеш, необходимо разобраться со слотами и блоками.

  • Слот — это период времени, в течение которого валидатор может создать блок 
  • Блок — это набор транзакций и метаданных, обрабатываемый валидатором. Метаданные каждого блока связывают его с предыдущим блоком, формируя цепочку.

Важно понимать, что слот длится от 400 до 600 мс и в каждом слоте валидатор может предложить новый блок. Если блок не создан, номер слота увеличивается и другой валидатор пытается создать новый блок. Это означает, что блоки связаны не со всеми слотами, но каждый блок связан со слотом, в котором он был предложен.

Итак, что такое блокхеш? Блокхеш — это уникальное хеш-значение всех записей реестра блокчейна, созданных в течение слота. Оно вычисляется на основе идентификатора последней записи блока. Каждый созданный блок получает уникальный блокхеш. Эти блокхеши используются как временные метки.

Какие уровни подтверждения используются в Solana?

Еще одно важное понятие — уровни подтверждения. Они отражают степень подтверждения конкретного блока сетью. Доступны три варианта: обработанный, подтвержденный и финализированный.

Когда валидатор отправляет блок в цепочку, блок получает статус обработанного. После того как необходимое количество валидаторов (66% валидаторов)  проголосует за включение блока, он добавляется в цепочку, а его уровень подтверждения меняется на подтвержденный. Когда поверх этого блока создается еще 31 блок, уровень подтверждения меняется на финализированный.

Почему возникают ошибки блокхеша?

Каждая транзакция содержит недавний блокхеш, который служит временной меткой. Транзакция истекает, когда этот блокхеш перестает считаться достаточно новым. Валидаторы, обрабатывающие транзакции, проверяют «BlockhashQueue» — список последних 300 блокхешей — и определяют, достаточно ли новый блокхеш у транзакции. Если блокхеша нет в списке, транзакция отклоняется. Поскольку слот обычно длится от 400 до 600 мс, блокхеш остается действительным в течение 60–90 секунд.

Блокхеш не найден (Transaction simulation failed: Blockhash not found)

Ошибки «Blockhash not found» возникают, когда при обработке транзакции валидатор считает указанный в ней блокхеш недействительным. Причина может заключаться в том, что блокхеш слишком старый или, в некоторых случаях, слишком новый. 

Чаще всего эта ошибка возникает, когда блокхеша из транзакции нет в очереди из последних 300 блокхешей, с которой валидатор выполняет сравнение. В результате возникает ошибка «blockhash not found». Срок действия транзакции может истечь, если она не была создана и обработана за отведенное время. Например, пользователь может слишком долго подписывать транзакцию. Возможна и другая ситуация: действительная транзакция отправлена, но не включена в текущий блок. Когда появляется возможность включить ее в следующий блок, блокхеш транзакции уже оказывается слишком старым.

В таких ситуациях, когда срок действия транзакции фактически истек, часто возникают ошибки превышения высоты блока (TransactionExpiredBlock heightExceededError). Чтобы лучше понять эту ошибку, нужно знать, что означает высота блока. Высота блока — это количество блоков перед текущим блоком. Если текущий блок имеет номер 1000, высота блока равна 1000. При создании транзакции указывается максимальная высота блока, до которой она будет действительна. Если во время обработки транзакции текущая высота блока превышает ее максимальную допустимую высоту, возникает ошибка.

Еще одна возможная причина ошибки «blockhash not found» — блокхеш транзакции новее блокхеша, используемого для проверки срока ее действия. Это может показаться запутанным, поэтому рассмотрим пример. Вы создаете транзакцию, указываете блокхеш определенного блока, например блока 1000, и сразу отправляете ее в RPC. RPC может получить блокхеш предыдущего блока, в данном случае блока 999, если RPC использует более высокий уровень подтверждения или RPC-нода отстает от сети. В результате блокхеш из транзакции не будет найден. Это необычная ситуация, но она может возникнуть в двух случаях:

1. Несовпадающие уровни подтверждения

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

Из-за этого блокхеш, используемый для проверки, может оказаться старее блокхеша транзакции, поскольку финализированные блоки «отстают» от последнего блока на 31 блок.

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

2. Несовпадающие RPC

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

Как исправить ошибки блокхеша

Для каждой из описанных выше ситуаций можно предпринять несколько действий.

1. Убедитесь, что транзакция отправляется с не слишком старым блокхешем:

При получении блокхеша транзакции используйте уровень подтверждения confirmed. Так в транзакцию будет включен более новый блокхеш, чем при использовании уровня finalized.

Для получения немного более нового блокхеша также можно использовать уровень подтверждения processed, однако примерно 5% обработанных блоков не финализируются кластером. В таком случае блокхеш транзакции будет относиться к отброшенной ветви и станет недействительным.

2. Убедитесь, что блокхеш транзакции не новее блокхеша, используемого для проверки ее действительности:

Всегда задавайте для preflightCommitment тот же уровень подтверждения, который использовался для получения блокхеша транзакции, даже при использовании skipPreflight. Так блокхеш транзакции не окажется новее блокхеша, используемого для проверки ее действительности.

Чтобы компенсировать отставание RPC-нод при отправке транзакций, следует повторно отправлять транзакции в RPC. Это можно делать через заданные интервалы. Тогда отстающий RPC со временем синхронизируется и обнаружит истечение срока действия транзакции.

Если используется запрос simulateTransaction, необходимо задать параметр replaceRecentBlockhash. Этот флаг указывает RPC заменить блокхеш симулируемой транзакции на блокхеш, который всегда будет действителен для симуляции.

3. Повторяйте отправку транзакции, пока блокхеш остается действительным:

При создании транзакции и получении последнего блокхеша следует сохранить значение lastValidBlockHeight для этого блокхеша. Затем можно повторно отправлять транзакцию с этим блокхешем, пока текущая высота блока не превысит допустимую высоту блока для транзакции. Чтобы непрерывно проверять действительность транзакции, можно использовать RPC-вызов getBlockHeight. Когда текущая высота блока станет больше высоты lastValidBlock транзакции, необходимо получить и использовать новый блокхеш.

Надеемся, эта статья помогла вам лучше понять ошибки блокхеша и способы сократить частоту их возникновения. Если у вас остались вопросы, присоединяйтесь к нашему сообществу в Discord или отправьте нам личное сообщение в X.

Дополнительные материалы:

‍

Подпишитесь на Helius

Следите за новостями разработки на Solana и получайте уведомления о наших публикациях

Увеличенное изображение